import math
import random
from pathlib import Path
from pandas import DataFrame
from lorem_text import lorem
from .constants import DUA_COLS, PERMITTED_USE_OR_DISCLOSURE, DATA_CLASSES


class DUAGenerator:
    """
    Colums:
        - dataCustodian: data custodian id (not name)
        - recipient: data recipient id (not name)
        - requestdData: three categories from DATA_CLAASS in settings.py
        - permittedUseOrDisclosure: one categories from PERMITTED_USE_OR_DISCLOSURE in settings.py

        * Values of columns below are lorem ipsum for now *
        - terms
        - terminationCause
        - terminationEffect
        - dataSecurityPlanAccess
        - dataSecurityPlanProtection
        - dataSecurityPlanStorage

    TODO:
        - Refactor two methods
    """

    def __init__(self):
        # self.organization_df = None  # not used in the current version of the code.
        # self.rows = None  # not used in the current version of the code.
        self.num_organization = None
        self.num_dua_org = None
        # self.num_requested_data = 1
        self.seed = None
        self.main_dataclass = None
        self.main_dataclass_portion = None
        self.main_permitted_use_or_disclosure = None
        self.main_permitted_use_or_disclosure_portion = None
        self.save_path = None

    def generate_dua(self):
        """
        This method generates DUA records in the simplest way. Organizations generated by this
        methods does not match any organizations in the Synthea dataset.

        Parameters
        ----------
        num_organization: int
            Total number of organizations to generate.
        save_path: str
            Path to save the generated DUA records.
        num_dua_org: int
            Number of organizations who has DUA with the data custodian.
        num_requested_data: int
            Number of requested data categories.
        seed: int
            Random seed for data generation consistency.
        """

        num_org_main_dataclass = int(
            math.floor(self.num_dua_org * self.main_dataclass_portion / 100)
        )
        num_org_main_permitted_use_or_disclosure = int(
            math.floor(
                self.num_dua_org * self.main_permitted_use_or_disclosure_portion / 100
            )
        )

        output_string = (
            "Generating DUA...\n"
            + f"Number of organizations: {self.num_organization}\n"
            + f"Number of DUA organizations: {self.num_dua_org}\n"
            + f"Main data class: {self.main_dataclass}\n"
            + f"Main data class portion: {self.main_dataclass_portion}\n"
            + f"Number of main data class organizations: {num_org_main_dataclass}\n"
            + f"Main permitted use or disclosure: {self.main_permitted_use_or_disclosure}\n"
            + f"Main permitted use or disclosure portion: {self.main_permitted_use_or_disclosure_portion}\n"
            + f"Number of main permitted use or disclosure organizations: {num_org_main_permitted_use_or_disclosure}\n"
            + f"Random seed: {self.seed}\n"
        )
        print(output_string)

        random.seed(self.seed)
        data_custodian_id = "data_custodian"
        data_custodian_dummy = [data_custodian_id for i in range(self.num_dua_org)]
        data_recipients_id = random.sample(
            list(range(self.num_organization)), self.num_dua_org
        )
        permittedUseOrDisclosure = [
            random.choice(PERMITTED_USE_OR_DISCLOSURE) for i in range(self.num_dua_org)
        ]
        # requested_data = [
        #     self.__get_requested_data(self.num_requested_data)
        #     for i in range(self.num_dua_org)
        # ]
        requested_data = [random.choice(DATA_CLASSES) for i in range(self.num_dua_org)]
        main_dataclass_org_index = random.sample(
            range(self.num_dua_org), num_org_main_dataclass
        )
        for index in main_dataclass_org_index:
            requested_data[index] = self.main_dataclass
        main_usage_org_index = random.sample(
            range(self.num_dua_org), num_org_main_permitted_use_or_disclosure
        )
        for index in main_usage_org_index:
            permittedUseOrDisclosure[index] = self.main_permitted_use_or_disclosure

        term = [lorem.sentence() for i in range(self.num_dua_org)]
        terminationEffect = [lorem.sentence() for i in range(self.num_dua_org)]
        terminationCause = [lorem.sentence() for i in range(self.num_dua_org)]
        storage = [lorem.sentence() for i in range(self.num_dua_org)]
        access = [lorem.sentence() for i in range(self.num_dua_org)]
        protection = [lorem.sentence() for i in range(self.num_dua_org)]

        dua_df = DataFrame(
            list(
                zip(
                    data_custodian_dummy,
                    data_recipients_id,
                    requested_data,
                    permittedUseOrDisclosure,
                    term,
                    terminationCause,
                    terminationEffect,
                    access,
                    protection,
                    storage,
                )
            ),
            columns=DUA_COLS,
        )

        dua_df.to_csv(f"{self.save_path}/dua.csv")
        with open(f"{self.save_path}/detail.txt", "w") as f:
            f.write(output_string)

        return dua_df

    def __get_recipients(self):
        # First organization (index 0) is the data custodian.
        # Refer to `get_data_custodian` method belwo.
        organizations = self.organization_df["Id"].tolist()
        return organizations[1:]

    def __get_data_custodian(self):
        return self.organization_df["Id"].tolist()[0]

    def __get_requested_data(self, num_requested_data: int = 3):
        requested_data = random.sample(DATA_CLASSES, num_requested_data)
        return ",".join(requested_data)

    def generate_dua_from_synthea(
        self, organization_df: DataFrame, save_path: str, num_requested_data: int = 3
    ):
        """
        WARNING: This method is not used in the current version of the code.

        This method generate DUA records from the Synthea dataset. It assumes that organizatoins
        in the Synthea dataset exchange data with each other. Therefore, every organization in the
        organizations csv will have DUA records.
        """
        self.organization_df = organization_df
        self.rows = organization_df.shape[0]

        data_custodian = self.__get_data_custodian()
        data_custodian_dummy = [data_custodian for i in range(self.rows)]
        random.seed(self.random_seed)
        permittedUseOrDisclosure = [
            random.choice(PERMITTED_USE_OR_DISCLOSURE) for i in range(self.rows)
        ]
        recipient = self.__get_recipients()

        requested_data = [
            self.__get_requested_data(num_requested_data) for i in range(self.rows)
        ]

        term = [lorem.sentence() for i in range(self.rows)]
        terminationEffect = [lorem.sentence() for i in range(self.rows)]
        terminationCause = [lorem.sentence() for i in range(self.rows)]
        storage = [lorem.sentence() for i in range(self.rows)]
        access = [lorem.sentence() for i in range(self.rows)]
        protection = [lorem.sentence() for i in range(self.rows)]

        dua_df = DataFrame(
            list(
                zip(
                    data_custodian_dummy,
                    recipient,
                    requested_data,
                    permittedUseOrDisclosure,
                    term,
                    terminationCause,
                    terminationEffect,
                    access,
                    protection,
                    storage,
                )
            ),
            columns=DUA_COLS,
        )

        dua_df.to_csv(save_path)

        return dua_df
