from functools import lru_cache
from urllib.parse import quote_plus

from pydantic import Field, ValidationInfo, computed_field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """Application settings loaded from environment variables (and optional `.env` file)."""

    model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    secret_key: str = "dev-only-change-in-production"
    algorithm: str = "HS256"
    access_token_expire_minutes: int = 60

    # HTTP route layout (wired in `main.py` and `routers/*/ __init__.py`)
    api_prefix: str = Field(
        default="api/v1",
        description="Leading path for all versioned API routes (slashes allowed, e.g. api/v1).",
    )
    admin_url_name: str = Field(
        default="admin",
        description="Single URL segment for the admin panel (no slashes), e.g. admin or nurse-admin.",
    )
    user_url_name: str = Field(
        default="user",
        description="Single URL segment for the user panel (no slashes).",
    )
    health_path: str = Field(
        default="health",
        description="Single URL segment for the health check endpoint.",
    )

    db_host: str = ""
    db_port: int = 5432
    db_name: str = ""
    db_user: str = ""
    db_password: str = ""

    smtp_host: str = ""
    smtp_port: int = 587
    smtp_user: str = ""
    smtp_password: str = ""
    smtp_from: str = "noreply@example.com"
    smtp_tls: bool = True

    @field_validator("api_prefix", mode="after")
    @classmethod
    def _strip_api_prefix(cls, v: str) -> str:
        v = v.strip().strip("/")
        if not v:
            raise ValueError("api_prefix must not be empty")
        return v

    @field_validator("admin_url_name", "user_url_name", "health_path", mode="after")
    @classmethod
    def _single_segment(cls, v: str, info: ValidationInfo) -> str:
        v = v.strip().strip("/")
        if not v or "/" in v:
            raise ValueError(
                f"{info.field_name} must be a single path segment without slashes "
                "(e.g. nurse-admin, user)."
            )
        return v

    @computed_field  # type: ignore[prop-decorator]
    @property
    def database_url(self) -> str:
        if not (self.db_host and self.db_host.strip()):
            return "sqlite:///./app.db"
        user = quote_plus(self.db_user)
        password = quote_plus(self.db_password)
        host = self.db_host.strip()
        name = self.db_name.strip() or "postgres"
        return (
            f"postgresql+psycopg://{user}:{password}@{host}:{self.db_port}/{name}"
        )

    @computed_field  # type: ignore[prop-decorator]
    @property
    def http_api_prefix(self) -> str:
        """Path prefix for versioned APIs, e.g. `/api/v1`."""
        return f"/{self.api_prefix}" if self.api_prefix else "/"

    @computed_field  # type: ignore[prop-decorator]
    @property
    def admin_router_prefix(self) -> str:
        """Admin router prefix, e.g. `/admin` or `/nurse-admin`."""
        return f"/{self.admin_url_name}"

    @computed_field  # type: ignore[prop-decorator]
    @property
    def user_router_prefix(self) -> str:
        return f"/{self.user_url_name}"

    @computed_field  # type: ignore[prop-decorator]
    @property
    def health_url_path(self) -> str:
        """Health route path, e.g. `/health`."""
        return f"/{self.health_path}"


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