from datetime import datetime
from typing import Tuple, Any

from sqlalchemy import Table, MetaData, Column

from sandwich.dialects import DialectHandler
from sandwich.modeling.dataclasses import ValidationResult
from sandwich.modeling.metadata import modeling_metadata

from .base_schema_generator import BaseSchemaGenerator


class MultiSat2SchemaGenerator(BaseSchemaGenerator):

    def __init__(self, dialect_handler: DialectHandler, validation_result: ValidationResult, entity_registration_date: datetime):
        super().__init__(dialect_handler, validation_result, entity_registration_date)
        self._on_make_procedures.append(self._make_sat_proc)

    def make_tables(self) -> dict[str, Table]:
        return {
            "sat": self.make_sat_table(),
        }

    def make_sat_table(self) -> Table:
        entity_name = self._validation_result.entity_name

        # Create sat table
        sat_table = Table(entity_name, MetaData(), schema="sat")

        # BKs
        for bk_key in self._validation_result.bk_keys:
            col = Column(bk_key[0], bk_key[1], nullable=False)
            sat_table.append_column(col)

        # HK
        hk_key = self._validation_result.hk_keys[0]
        sat_table.append_column(Column(hk_key[0], hk_key[1], primary_key=True))

        # LoadDate
        load_date = modeling_metadata.loaddate
        load_date_type = self._validation_result.system_column_types[load_date]
        load_date_col = Column(load_date, load_date_type, primary_key=True)
        sat_table.append_column(load_date_col)

        # RecordSource
        record_source = modeling_metadata.recordsource
        record_source_type = self._validation_result.system_column_types[record_source]
        record_source_col = Column(record_source, record_source_type, nullable=False)
        sat_table.append_column(record_source_col)

        # HashDiff
        hashdiff_source = modeling_metadata.hashdiff
        hashdiff_type = self._validation_result.system_column_types[hashdiff_source]
        hashdiff_col = Column(hashdiff_source, hashdiff_type, nullable=False)
        sat_table.append_column(hashdiff_col)

        for (name_, type_) in self._validation_result.business_column_types.items():
            col = Column(name_, type_, nullable=True)
            sat_table.append_column(col)

        # IsAvailable
        is_available_source = modeling_metadata.is_available
        is_available_type = self._validation_result.system_column_types[is_available_source]
        is_available_col = Column(is_available_source, is_available_type, nullable=False)
        sat_table.append_column(is_available_col)

        return sat_table

    def _make_sat_proc(self, procedures: dict[str, Tuple[str, str, str]], tables: dict[str, Table]) -> None:
        sat_proc_code, sat_proc_name, sat_call_stmt = self.dialect_handler.make_scd2_sat_proc(
            sat_table=tables["sat"],
            hk_name=self._validation_result.hk_keys[0][0],
            hashdiff_col=modeling_metadata.hashdiff,
            is_available_col=modeling_metadata.is_available,
            loaddate_col=modeling_metadata.loaddate,
            stg_schema=self._validation_result.stg_schema,
            header=self.header
        )
        procedures["sat"] = (sat_proc_code, sat_proc_name, sat_call_stmt)