from datetime import datetime
from typing import Tuple

from sqlalchemy import Table, MetaData, Column, UniqueConstraint

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 HubSchemaGenerator(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_hub_proc)

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

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

        hub_table = Table(entity_name, MetaData(), schema="hub")
        uks: list[str] = []

        # BKs
        for bk_key in self._validation_result.bk_keys:
            uks.append(bk_key[0])
            bk_col = Column(bk_key[0], bk_key[1], nullable=False)
            hub_table.append_column(bk_col)
        hub_table.append_constraint(UniqueConstraint(*uks))

        # HK
        hk_key = self._validation_result.hk_keys[0]
        hub_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, nullable=False)
        hub_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)
        hub_table.append_column(record_source_col)

        return hub_table

    def _make_hub_proc(self, procedures: dict[str, Tuple[str, str, str]], tables: dict[str, Table]) -> None:
        hub_proc_code, hub_proc_name, hub_call_stmt = self.dialect_handler.make_hub_proc(
            hub_table=tables["hub"],
            bk_keys=self._validation_result.bk_keys,
            header=self.header
        )
        procedures["hub"] = (hub_proc_code, hub_proc_name, hub_call_stmt)