"""
Objects to generate SQL requests.

The QueryWithParameter and QueryWithParameterGroupedByCriteria,
"""
from abc import ABC, abstractmethod


class QueryGenerator(ABC):
    @abstractmethod
    def __init__(self): ...

    @abstractmethod
    def __str__(self) -> str: ...


class QueryWithParameter(QueryGenerator):
    # DEFAULT_AUTHORIZED_PARAMETER_VALUES: tuple[str, ...] = ("foo", "bar")

    def __init__(self, parameter: str|None =None,
                 authorized_parameter_values: tuple[str, ...] | None = None,
                 summed_attribute: str|None =None):
        if parameter is None: raise ValueError
        self.parameter = str(parameter)

        if authorized_parameter_values is None:
            authorized_parameter_values = self.DEFAULT_AUTHORIZED_PARAMETER_VALUES
        self.authorized_parameter_values = authorized_parameter_values

        if summed_attribute is None: raise ValueError
        self.summed_attribute = str(summed_attribute)

    @property
    def parameter(self) -> str:
        return self.__parameter

    @parameter.setter
    def parameter(self, value):
        self.__parameter = str(value)

    def __force_typing_on_authorized_parameter_values(self):
        self.__authorized_parameter_values = tuple(
                map(str, self.__authorized_parameter_values))

    @property
    def authorized_parameter_values(self) -> tuple[str, ...]:
        return self.__authorized_parameter_values

    @authorized_parameter_values.setter
    def authorized_parameter_values(self, value):
        self.__authorized_parameter_values = value
        self.__force_typing_on_authorized_parameter_values()



class QueryWithParameterGroupedByCriteria(QueryWithParameter):

    def __init__(self, parameter: str|None =None,
                 authorized_parameter_values: tuple[str, ...] | None =None,
                 criteria: str | None =None,
                 summed_attribute: str|None =None
                 ):
        if parameter is None: raise ValueError
        self.parameter = str(parameter)

        if authorized_parameter_values is None:
            authorized_parameter_values = self.DEFAULT_AUTHORIZED_PARAMETER_VALUES
        self.authorized_parameter_values = authorized_parameter_values

        self.criteria = criteria

        if summed_attribute is None: raise ValueError
        self.summed_attribute = str(summed_attribute)

    @property
    def criteria(self) -> str | None:
        return self.__criteria

    @criteria.setter
    def criteria(self, value: str | None):
        if value is None:
            self.__criteria = None
        else:
            self.__criteria = str(value)

    def __repr__(self):
        return f"QueryGenerator(parameter={self.parameter}, authorized_parameter_values={self.authorized_parameter_values}, criteria={self.criteria}, summed_attribute={self.summed_attribute})"


class QueryFlight:
    DEFAULT_AUTHORIZED_PARAMETER_VALUES: tuple[str, ...] = (
            "ATL", "ORD", "DFW", "DEN", "LAX", "IAH", "LAS", "SFO", "PHX", "MCO",
            "SEA", "CLT", "MSP", "LGA", "DTW", "EWR", "BOS", "BWI", "SLC", "JFK")

class QuerySSB:
    # DEFAULT_AUTHORIZED_PARAMETER_VALUES: tuple[str, ...] = tuple(CSS4_COLORS.keys())
    # defaults to None to accept all values
    DEFAULT_AUTHORIZED_PARAMETER_VALUES = None

class QueryFlightWithParameterGroupedByCriteria(QueryWithParameterGroupedByCriteria,
                                                QueryFlight):

    # def __init__(self, parameter: str = "departure_airport",
    #              authorized_parameter_values: tuple[str, ...] | None = None,
    #              criteria: str | None = None,
    #              summed_attribute: str = "nb_flights"):
    #     self.parameter = str(parameter)

    #     if authorized_parameter_values is None:
    #         authorized_parameter_values = self.DEFAULT_AUTHORIZED_PARAMETER_VALUES
    #     self.authorized_parameter_values = authorized_parameter_values

    #     self.criteria = criteria

    #     self.summed_attribute = str(summed_attribute)

    def __str__(self):
        if self.criteria is None:
            raise ValueError("attribute criteria has not been set.")
        res = f"""
        SELECT {self.parameter}, {self.criteria}, SUM({self.summed_attribute})
        FROM fact_table
        INNER JOIN airport_dim ON airport_dim.iata_code = fact_table.departure_airport
        NATURAL JOIN hour_dim
        INNER JOIN time_dim ON time_dim.day = fact_table.date
        """
        if self.authorized_parameter_values is not None:
            res += f"WHERE {self.parameter} IN {self.authorized_parameter_values}\n"
        res += f"""
        GROUP BY {self.parameter}, {self.criteria}
        ORDER BY SUM({self.summed_attribute}) DESC;
        """
        return res

class QueryFlightWithParameter(QueryWithParameter, QueryFlight):
    def __str__(self):
        return f"""
        SELECT {self.parameter}, SUM({self.summed_attribute})
        FROM fact_table
        WHERE {self.parameter} IN {self.authorized_parameter_values}
        GROUP BY {self.parameter}
        ORDER BY SUM({self.summed_attribute}) DESC;
        """

class QuerySSBWithParameterGroupedByCriteria(QueryWithParameterGroupedByCriteria,
                                             QuerySSB):
    def __str__(self):
        if self.criteria is None:
            raise ValueError("attribute criteria has not been set.")
        res = f"""
        SELECT {self.parameter}, {self.criteria}, SUM({self.summed_attribute})
        FROM lineorder
        """
        # join to tables according to which columns are needed
        if self.criteria.startswith('c_') or self.parameter.startswith('c_'):
            res += "INNER JOIN customer ON lo_custkey = c_custkey\n"
        if self.criteria.startswith('p_') or self.parameter.startswith('p_'):
            res += "INNER JOIN part ON lo_partkey = p_partkey\n"
        if self.criteria.startswith('s_') or self.parameter.startswith('s_'):
            res += "INNER JOIN supplier ON lo_suppkey = s_suppkey\n"
        if self.criteria.startswith('D_') or self.parameter.startswith('D_'):
            res += "INNER JOIN date ON lo_orderdate = D_DATEKEY\n"

        if self.authorized_parameter_values is not None:
            res += "WHERE {self.parameter} IN {self.authorized_parameter_values}\n"


        res += f"""
        GROUP BY {self.parameter}, {self.criteria}
        ORDER BY SUM({self.summed_attribute}) DESC;
        """
        return res

class QuerySSBWithParameter(QueryWithParameter,
                            QuerySSB):
    def __str__(self):
        res = f"""
        SELECT {self.parameter}, SUM({self.summed_attribute})
        FROM lineorder
        """
        if self.authorized_parameter_values is not None:
            res += f"WHERE {self.parameter} IN {self.authorized_parameter_values}\n"
        res += f"""
        GROUP BY {self.parameter}
        ORDER BY SUM({self.summed_attribute}) DESC;
        """
        return res
