import os
from pathlib import Path
from typing import Union, Optional

import pandas as pd
from statsmodels.regression.linear_model import RegressionResults

from src.analysis_functions.regression import Regression
from src.data_loading_and_saving.print_and_save_results import print_and_save_result
from src.utils.helper_functions import (
    calculate_inverse_bayes_correction,
    transform_to_bayes_corrected_valence,
)


class InfluenceOfUpAndDownvotesOnReplies:
    """
    A class used to perform a dataset specific analysis on the influence of upvotes and downvotes
    on the number of replies based on a linear regression model for totalvotes and valence

    Attributes
    ----------
    print_result: bool
        A flag used to indicate if the function should print the result to the standard output.
    save_result: bool
        A flag used to indicate if the function should save the result to a file.
    filepath: str
        The directory path where the result files will be saved if save_result is True.
    ...

    Methods
    -------
    report_increase_per_up_and_downvote_from_totalvotes_and_valence
        (data: pd.DataFrame, weight_as_distribution_quantile: bool, weight_m: float, model: RegressionResults,
        step: list, startpoint: Union[str, list], name_save_file: Optional[Path]) -> None:
        Gives the % increase in reply likelihood for a given step in upvotes and downvotes
        according to a linear regression model
    """

    def __init__(
        self,
        print_result: bool = True,
        save_result: bool = True,
        filepath: str = "results/",
    ):
        """
        Constructs all the necessary attributes for the InfluenceOfUpAndDownvotesOnReplies object.

        Parameters
        ----------
        print_result: bool
            A flag to determine if the result should be printed.
        save_result: bool
            A flag to determine if the result should be saved.
        filepath: str
            The directory where the result should be saved.
        """
        self.print_result: bool = print_result
        self.save_result: bool = save_result
        self.filepath: str = filepath
        if not os.path.isdir(filepath):
            os.makedirs(filepath)

    def report_increase_per_up_and_downvote_from_totalvotes_and_valence(
        self,
        data: pd.DataFrame,
        weight_as_distribution_quantile: bool,
        weight_m: float,
        model: RegressionResults,
        step: list = None,
        startpoint: Union[str, list] = "average",
        name_save_file: Optional[Path] = None,
    ) -> None:
        """
        Reports the percentage increase in reply likelihood for specified upvote and downvote steps
        from a given startpoint, using a linear regression model.

        Parameters
        ----------
        data : pd.DataFrame
            The dataset containing 'totalvotes', 'valence', and other relevant metrics.
        weight_as_distribution_quantile : bool
            Determines if weight_m should be treated as a quantile value for weighting totalvotes.
        weight_m : float
            The weight factor or quantile value for calculating weighted measures.
        model : RegressionResults
            The fitted linear regression model used for prediction.
        step : list, optional
            A list containing the step increase for upvotes and downvotes respectively.
        startpoint : Union[str, list], optional
            The starting point for calculation. Can be 'average' or a list of [totalvotes, bayes_corrected_valence].
        name_save_file : Optional[Path], optional
            The name of the file to save the result to.

        Returns
        -------
        None
        """
        startpoint_totalvotes: int = 0
        startpoint_bayes_corrected_valence: int = 0
        average_valence: float = data["valence"].mean()

        if isinstance(startpoint, str) and startpoint.lower() == "average":
            startpoint_totalvotes: float = data["totalvotes"].mean()
            startpoint_bayes_corrected_valence: float = data[
                "bayes-corrected (q=0.25) valence"
            ].mean()

        if isinstance(startpoint, list):
            startpoint_totalvotes: Union[int, float] = startpoint[0]
            startpoint_bayes_corrected_valence: Union[int, float] = startpoint[1]

        else:
            ValueError("startpoint must be a valid point or 'average'")

        if weight_as_distribution_quantile:
            weight_m: float = data["totalvotes"].quantile(q=weight_m)

        non_bayes_corrected_valence: float = (
            calculate_inverse_bayes_correction(
                bayes_corrected_value=startpoint_bayes_corrected_valence,
                volume=startpoint_totalvotes,
                weight_factor_m=weight_m,
                average_measure=average_valence,
            )
        )

        downvote_equivalent_average_bayes_corrected_valence: float = (
                - (non_bayes_corrected_valence - 0.5) * startpoint_totalvotes
        )

        average_bayes_corrected_negativty_plus: float = transform_to_bayes_corrected_valence(
            upvotes=startpoint_totalvotes
            - downvote_equivalent_average_bayes_corrected_valence
            + step[0],
            downvotes=downvote_equivalent_average_bayes_corrected_valence + step[1],
            average_valence=average_valence,
            weight_factor_m=weight_m,
        )

        increase_per_step: float = (
            Regression().predict_percentage_increase_between_liner_model_points(
                model=model,
                data_point_1=[
                    startpoint_bayes_corrected_valence,
                    startpoint_totalvotes,
                ],
                data_point_2=[
                    average_bayes_corrected_negativty_plus,
                    startpoint_totalvotes + step[0] + step[1],
                ],
            )
        )

        result = f"""
        your startpoint was: bayes-correced-valence {startpoint_bayes_corrected_valence}, totalvotes {startpoint_totalvotes}
        for a step with {step[0]} upvotes and {step[1]} downvotes increase
        you obtain an endpoint of: bayes-correced-valence {average_bayes_corrected_negativty_plus}, totalvotes {startpoint_totalvotes + step[0] + step[1]}
        the increase in replies is {increase_per_step} %
        """

        print_and_save_result(
            self.print_result,
            self.save_result,
            self.filepath,
            result,
            name_save_file,
        )
