import os
from functools import cached_property, lru_cache
from pathlib import Path
from tempfile import mkdtemp
from typing import Any, Literal, Optional, Self

from alembic import command
from alembic.config import Config
from pydantic import HttpUrl, model_validator
from pydantic_extra_types.semantic_version import SemanticVersion
from pydantic_settings import (
    BaseSettings,
    PydanticBaseSettingsSource,
    SettingsConfigDict,
    TomlConfigSettingsSource,
)
from xplan_tools.interface.db import AsyncDBRepository, DBRepository
from xplan_tools.model.base import Appschema

from xmas_app.models.mappings import AssociationTableMapping


class Settings(BaseSettings):
    debug: bool = False

    PGUSER: Optional[str] = None
    PGPASSWORD: Optional[str] = None
    PGHOST: Optional[str] = None
    PGPORT: Optional[str] = None
    PGDATABASE: Optional[str] = None
    PGSERVICE: Optional[str] = None

    appschema: str
    appschema_version: str
    appschema_cls: Appschema | None = None
    app_port: int
    db_type: str = "postgres"  # defaulting to postgres
    app_mode: Literal["dev", "prod"] = "prod"
    codelist_repo: HttpUrl = HttpUrl(
        "https://registry.gdi-de.org/codelist/de.xleitstelle.xplanung"
    )
    qgis_plugin_name: str = "XMAS-Plugin"
    qgis_plugin_min_version: SemanticVersion = SemanticVersion(major=0, minor=19)
    mappings_toml: str = str(Path(__file__).parent / "resources/mappings.toml")
    generate_schriftinhalt: bool = True
    static_file_dir: Path = Path(mkdtemp(prefix="xmas_"))

    model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")

    @model_validator(mode="after")
    def _after_validator(self) -> Self:
        self.appschema_cls = Appschema.from_prefix(
            self.appschema, self.appschema_version
        )
        return self

    def model_post_init(self, context: Any) -> None:
        """Post-initialization steps."""
        os.environ.update(
            self.model_dump(
                include={
                    "PGUSER",
                    "PGHOST",
                    "PGPASSWORD",
                    "PGPORT",
                    "PGDATABASE",
                    "PGSERVICE",
                },
                exclude_none=True,
            )
        )
        self.static_file_dir.mkdir(exist_ok=True)

    @cached_property
    def repo(self):
        """Initialize and return repo."""
        return DBRepository(
            "postgresql://",
        )

    @cached_property
    def async_repo(self):
        """Initialize and return async repo."""
        return AsyncDBRepository(
            "postgresql://",
        )


@lru_cache
def get_settings() -> Settings:
    return Settings()


class Mappings(BaseSettings):
    association_table: AssociationTableMapping

    model_config = SettingsConfigDict(toml_file=get_settings().mappings_toml)

    @classmethod
    def settings_customise_sources(
        cls,
        settings_cls: type[BaseSettings],
        init_settings: PydanticBaseSettingsSource,
        env_settings: PydanticBaseSettingsSource,
        dotenv_settings: PydanticBaseSettingsSource,
        file_secret_settings: PydanticBaseSettingsSource,
    ) -> tuple[PydanticBaseSettingsSource, ...]:
        return (TomlConfigSettingsSource(settings_cls),)


@lru_cache
def get_mappings() -> Mappings:
    return Mappings()


def initialize_db() -> None:
    """Ensure DB repository is initialized and runs app-specific alembic migrations."""
    repo = get_settings().repo
    alembic_cfg = Config()
    alembic_cfg.set_main_option("script_location", "xmas_app:models:migrations")
    with repo._engine.connect() as connection:
        alembic_cfg.attributes["connection"] = connection
        command.upgrade(alembic_cfg, "head")
