from dataclasses import dataclass
from datetime import datetime
from typing import Any, Tuple

from sqlalchemy import Table, Engine, Connection, MetaData

@dataclass(frozen=True)
class Dv2Entity:
    entity_name: str
    template: str
    created_on: datetime


@dataclass(frozen=True)
class StgInfo:
    stg_name: str
    stg_schema: str
    hk_keys: dict[str, Any]
    bk_keys: dict[str, Any]
    sys_columns: dict[str, Any]
    bus_columns: dict[str, Any]
    degenerate_field: Tuple[str, Any] | None = None

@dataclass(frozen=True)
class Dv2SystemInfo:
    entities_list: list[Dv2Entity]
    sys_entities: Table

class Dv2ModelingMetadata:
    HEADER_TEMPLATE = """/* 
    =====================================================================
    AUTO-GENERATED CODE — DO NOT EDIT MANUALLY
    =====================================================================

    This stored procedure was automatically generated by:
      sandwich (https://pypi.org/project/sandwich/)

    Manual changes are discouraged.
    This file may be regenerated at any time, and all manual edits
    will be overwritten.

    Created on   : {created_on:%Y-%m-%d %H:%M:%S}
    Updated on   : {updated_on:%Y-%m-%d %H:%M:%S}
    Generator    : sandwich v{version}
    Entity name  : {entity_name}

    =====================================================================
*/
"""
    loaddate = "LoadDate"
    recordsource = "RecordSource"
    hashdiff = "HashDiff"
    is_available = "IsAvailable"
    names: list[str] = [loaddate, recordsource, hashdiff, is_available]
    # _dialects_config: dict[str, dict[str, str]] = {
    #     "mssql": {
    #         loaddate: "DATETIME2",
    #         recordsource: "VARCHAR(200)",
    #         hashdiff: "CHAR(40)",
    #         is_available: "BIT",
    #     },
    #     "postgres": {
    #         loaddate: "TIMESTAMP",
    #         recordsource: "TEXT",
    #         hashdiff: "CHAR(40)",
    #         is_available: "BOOLEAN",
    #     },
    # }
    def __init__(self):
        #self.dialect = dialect

        #self.column_types = self._dialects_config[self.dialect]
        self.required_columns: list[str] = [self.loaddate, self.recordsource]

modeling_metadata = Dv2ModelingMetadata()

def get_stg_info(entity_name: str, schema: str, conn: Engine | Connection) -> StgInfo:
    stg = Table(entity_name, MetaData(), schema=schema, autoload_with=conn)

    hk_keys: dict[str, Any] = {}
    bk_keys: dict[str, Any] = {}
    sys_columns: dict[str, Any] = {}
    bus_columns: dict[str, Any] = {}
    degenerate_field: Tuple[str, Any] | None = None

    for col in stg.columns.values():
        if col.name.startswith("hk_"): # hash key
            hk_keys[col.name] = col.type
        elif col.name.startswith("bk_"): # business key
            bk_keys[col.name] = col.type
        elif col.name.startswith("dg_"): # degenerate field (transactional links only)
            degenerate_field = (col.name, col.type)
        elif col.name.startswith("sg_"): # surrogate key
            raise Exception(f"sg column '{col.name}' is not implemented yet")
        elif col.name in modeling_metadata.names:
            # type_name = metadata.column_types[col.name]
            # if not str(col.type).startswith(type_name):
            #     raise Exception(f"{col.name} column must be `{type_name}` type, but it is `{str(col.type)}`")
            sys_columns[col.name] = col.type
        else:
            bus_columns[col.name] = col.type

    return StgInfo(
        stg_name=stg.name,
        stg_schema=stg.schema,
        hk_keys=hk_keys,
        bk_keys=bk_keys,
        sys_columns=sys_columns,
        bus_columns=bus_columns,
        degenerate_field=degenerate_field,
    )

def infer_template(stg_info: StgInfo):
    hk_count = len(stg_info.hk_keys)
    if hk_count == 0:
        raise Exception("hk column is required for `scd2dim` validation")
    elif hk_count > 1:
        return "link2fact"
    else:
        return "scd2dim"