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
    bcrypt_rounds: int = Field(default=10, ge=4, le=14)

    # 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 = ""
    db_pool_size: int = Field(
        default=10,
        ge=1,
        le=50,
        description="SQLAlchemy pool size for PostgreSQL (ignored for SQLite).",
    )
    db_max_overflow: int = Field(
        default=20,
        ge=0,
        le=100,
        description="Extra connections beyond pool_size before pool timeout.",
    )
    db_pool_recycle: int = Field(
        default=1800,
        ge=0,
        description="Recycle pooled connections after N seconds (0 = disabled).",
    )
    db_pool_timeout: int = Field(
        default=30,
        ge=1,
        description="Seconds to wait for a free connection before TimeoutError.",
    )

    smtp_host: str
    smtp_port: int
    smtp_user: str
    smtp_password: str
    smtp_from: str
    smtp_tls: bool
    admin_notify_email: str = Field(
        default="",
        description="Email address that receives contact form submissions. Defaults to SMTP_FROM.",
    )

    community_upload_dir: str = Field(
        default="uploads",
        description="Directory for community media (relative to project root, or absolute path).",
    )
    community_uploads_url_segment: str = Field(
        default="uploads",
        description="URL segment to serve uploaded files, e.g. uploads → /api/v1/uploads/...",
    )
    community_max_upload_mb: int = Field(default=20, ge=1, le=200)

    @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()
