"""
PyGitWeb configuration: env-driven settings via pydantic-settings, constants, and loadavg.
"""

from __future__ import annotations

import os
import sys
from pathlib import Path

from pydantic import Field, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict

# Actions (most identical to gitweb.perl actions)
ACTIONS: set[str] = {
    "blame",
    "blame_incremental",
    "blame_data",
    "blame_raw",
    "blobdiff",
    "blobpatch",
    "blob",
    "blob_plain",
    "commitdiff",
    "commit",
    "heads",
    "history",
    "log",
    "patch",
    "patches",
    "remotes",
    "rss",
    "atom",
    "search",
    "search_help",
    "shortlog",
    "summary",
    "tag",
    "tags",
    "tree",
    "snapshot",
    "object",
    "opml",
    "project_list",
    "project_index",
}

# Map file extension to highlight.js language
BLOB_LANG: dict[str, str] = {
    "py": "python",
    "js": "javascript",
    "ts": "typescript",
    "jsx": "javascript",
    "tsx": "typescript",
    "html": "html",
    "htm": "html",
    "css": "css",
    "scss": "scss",
    "json": "json",
    "md": "markdown",
    "sh": "bash",
    "bash": "bash",
    "yml": "yaml",
    "yaml": "yaml",
    "xml": "xml",
    "go": "go",
    "rs": "rust",
    "java": "java",
    "c": "c",
    "h": "c",
    "cpp": "cpp",
    "cc": "cpp",
    "cxx": "cpp",
    "sql": "sql",
    "r": "r",
    "rb": "ruby",
    "php": "php",
    "swift": "swift",
    "kt": "kotlin",
    "vue": "xml",
    "toml": "toml",
    "ini": "ini",
    "cfg": "ini",
    "dockerfile": "dockerfile",
}


class Settings(BaseSettings):
    """
    Runtime settings from ~/.pygitweb/settings.json (or PYGITWEB_SETTINGS_CONFIG),
    PYGITWEB_* env vars, and optionally a .env file. On first run the file is created
    from the environment. When the file exists, env vars override file values (with a warning).

    When AUTH is true, PyGitWeb uses OAuth2 password flow (POST /token), Bearer tokens,
    and browser login. Local credentials live in the auth JSON file (see PYGITWEB_AUTH_CONFIG).

    If PYGITWEB_AUTH is omitted entirely (or empty), authentication defaults to enabled
    and a random password is written to the auth config file on first run (see stderr).
    Set PYGITWEB_AUTH=0 or PYGITWEB_AUTH=1 explicitly as needed.
    """

    model_config = SettingsConfigDict(
        env_prefix="PYGITWEB_",
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False,
        validate_assignment=True,
        extra="ignore",
    )

    PROJECTROOT: str = Field(default_factory=lambda: str(Path.home()))
    PROJECTS_LIST: str = ""  # falls back to PROJECTROOT when blank
    PROJECT_MAXDEPTH: int = 1
    SITE_NAME: str = "PyGitWeb"
    EXPORT_OK: str = ""
    LIST_ALL: bool = True
    STRICT_EXPORT: bool = False
    GIT: str = "git"
    MAXLOAD: float | None = None

    AUTH: bool | None = None
    AUTH_CONFIG: str = ""
    SETTINGS_CONFIG: str = ""
    ENABLED_PLUGINS: list[str] = Field(default_factory=list)
    API_KEY_MAX_COUNT: int = Field(default=2, ge=0)
    API_KEY_EXPIRY_DAYS: int = Field(
        default=0,
        ge=0,
        description="Lifetime of a newly issued API key, in days. 0 means the key does not expire.",
    )

    @field_validator("AUTH", mode="before")
    @classmethod
    def _coerce_auth(cls, v: object) -> bool | None:
        if v is None:
            return None
        if isinstance(v, str) and v.strip() == "":
            return None
        if isinstance(v, bool):
            return v
        if isinstance(v, int):
            return v != 0
        if isinstance(v, str):
            s = v.strip().lower()
            if s in ("none", "false", "0", "no", "off"):
                return False
            if s in ("true", "1", "yes", "on"):
                return True
            return None
        return bool(v)

    @model_validator(mode="after")
    def _defaults(self) -> Settings:
        if not self.PROJECTS_LIST:
            self.PROJECTS_LIST = self.PROJECTROOT
        return self


def _finalize_auth_settings(s: Settings) -> None:
    implicit_auth = s.AUTH is None
    if implicit_auth:
        s.AUTH = True
    if implicit_auth and s.AUTH:
        cfg_path = Path(s.AUTH_CONFIG).expanduser() if s.AUTH_CONFIG else Path.home() / ".pygitweb" / "auth.json"
        print(
            "WARNING: PyGitWeb: PYGITWEB_AUTH was not set; authentication defaults to ON.\n"
            "         Set PYGITWEB_AUTH=0 to disable, or PYGITWEB_AUTH=1 and configure "
            f"{cfg_path}.\n",
            file=sys.stderr,
        )


from pygitweb.settings_config import init_settings  # noqa: E402

settings = init_settings()

from pygitweb.auth_config import init_auth_config  # noqa: E402

auth_config = init_auth_config(settings)


def get_loadavg() -> float:
    """First element of load average, or 0 if unavailable. Port of get_loadavg."""
    if sys.platform != "win32":
        try:
            return os.getloadavg()[0]
        except OSError:
            pass
    try:
        with open("/proc/loadavg") as f:
            return float(f.read().split()[0])
    except (OSError, ValueError):
        return 0.0