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

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 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=17)
    mappings_toml: str = str(Path(__file__).parent / "resources/mappings.toml")

    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
        )
        self.repo
        return self

    @cached_property
    def repo(self):
        """Set environment variable, initialize and return repo."""
        os.environ.update(
            self.model_dump(
                include={
                    "PGUSER",
                    "PGHOST",
                    "PGPASSWORD",
                    "PGPORT",
                    "PGDATABASE",
                    "PGSERVICE",
                },
                exclude_none=True,
            )
            | {"PGCONNECT_TIMEOUT": "5"}  # add a default timeout
        )
        return DBRepository(
            "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()
