"""Application settings, loaded from environment / .env."""
from functools import lru_cache
from typing import Annotated, List
from urllib.parse import quote_plus

from pydantic import field_validator
from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=".env", env_file_encoding="utf-8", extra="ignore"
    )

    # --- App ---
    APP_NAME: str = "ISP Management System"
    ENV: str = "development"
    DEBUG: bool = True
    API_V1_PREFIX: str = "/api/v1"
    TIMEZONE: str = "Asia/Dhaka"

    # --- Database ---
    POSTGRES_HOST: str = "localhost"
    POSTGRES_PORT: int = 5432
    POSTGRES_USER: str = "isp"
    POSTGRES_PASSWORD: str = "isp"
    POSTGRES_DB: str = "isp_db"
    DATABASE_URL: str | None = None
    # Shared hosting (cPanel) caps database connections server-wide; the cPanel
    # .env sets these low so Passenger workers cannot exhaust that budget.
    POSTGRES_POOL_SIZE: int = 10
    POSTGRES_MAX_OVERFLOW: int = 20

    # --- Auth ---
    SECRET_KEY: str = "change-me-in-production"
    JWT_ALGORITHM: str = "HS256"
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 8
    REFRESH_TOKEN_EXPIRE_DAYS: int = 14

    # --- Bootstrap admin (created by seed) ---
    FIRST_ADMIN_EMAIL: str = "admin@isp.local"
    FIRST_ADMIN_PASSWORD: str = "Admin@12345"
    FIRST_ADMIN_NAME: str = "System Administrator"

    # Demo staff logins (manager / technicians / auditor) share the admin password, so
    # seeding them into a real deployment would hand out four working accounts. Off by
    # default; development turns it on explicitly.
    SEED_DEMO_STAFF: bool = False

    # --- Startup behaviour ---
    # Honoured by the cPanel/Passenger entrypoint (deploy/cpanel/passenger_wsgi.py);
    # the Docker entrypoint reads the same names straight from the environment.
    RUN_MIGRATIONS: bool = True
    RUN_SEED: bool = False

    # --- Uploads ---
    UPLOAD_DIR: str = "uploads"
    MAX_UPLOAD_MB: int = 10

    # --- Static SPA (single-process deployments such as cPanel/Passenger) ---
    # When set to a directory containing the built frontend, FastAPI serves it with
    # SPA fallback. Left unset under Docker, where nginx serves the frontend instead.
    STATIC_DIR: str | None = None

    # --- Auth rate limiting (in-process; nginx owns this under Docker Compose) ---
    AUTH_RATE_LIMIT_PER_MINUTE: int = 10
    AUTH_RATE_LIMIT_BURST: int = 5

    # --- CORS ---
    # NoDecode: keep the raw string so the validator below can split on commas.
    CORS_ORIGINS: Annotated[List[str], NoDecode] = ["http://localhost:5173", "http://localhost"]

    @field_validator("CORS_ORIGINS", mode="before")
    @classmethod
    def _split_origins(cls, v):
        if isinstance(v, str):
            return [o.strip() for o in v.split(",") if o.strip()]
        return v

    @property
    def sqlalchemy_url(self) -> str:
        if self.DATABASE_URL:
            return self.DATABASE_URL
        user = quote_plus(self.POSTGRES_USER)
        password = quote_plus(self.POSTGRES_PASSWORD)
        return (
            f"postgresql+psycopg://{user}:{password}"
            f"@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
        )


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


settings = get_settings()
