from __future__ import annotations

import os
from dataclasses import dataclass
from datetime import timedelta
from functools import lru_cache

from typing import Optional

from services.pricing import PricingMetadata

from services.security import JWTSettings


@dataclass(frozen=True, slots=True)
class AdminBootstrapSettings:
    """Default administrator bootstrap configuration."""

    email: str
    username: str
    password: str
    roles: tuple[str, ...]
    force_reset: bool


@dataclass(frozen=True, slots=True)
class SessionSettings:
    """Cookie and header configuration for session token transport."""

    access_cookie_name: str
    refresh_cookie_name: str
    cookie_secure: bool
    cookie_domain: Optional[str]
    cookie_path: str
    header_name: str
    header_prefix: str
    allow_header_fallback: bool


@dataclass(frozen=True, slots=True)
class Settings:
    """Application configuration sourced from environment variables."""

    jwt_secret_key: str = "change-me"
    jwt_algorithm: str = "HS256"
    jwt_access_token_minutes: int = 15
    jwt_refresh_token_days: int = 7
    session_access_cookie_name: str = "calminer_access_token"
    session_refresh_cookie_name: str = "calminer_refresh_token"
    session_cookie_secure: bool = False
    session_cookie_domain: Optional[str] = None
    session_cookie_path: str = "/"
    session_header_name: str = "Authorization"
    session_header_prefix: str = "Bearer"
    session_allow_header_fallback: bool = True
    admin_email: str = "admin@calminer.local"
    admin_username: str = "admin"
    admin_password: str = "ChangeMe123!"
    admin_roles: tuple[str, ...] = ("admin",)
    admin_force_reset: bool = False
    pricing_default_payable_pct: float = 100.0
    pricing_default_currency: str | None = "USD"
    pricing_moisture_threshold_pct: float = 8.0
    pricing_moisture_penalty_per_pct: float = 0.0

    @classmethod
    def from_environment(cls) -> "Settings":
        """Construct settings from environment variables."""

        return cls(
            jwt_secret_key=os.getenv("CALMINER_JWT_SECRET", "change-me"),
            jwt_algorithm=os.getenv("CALMINER_JWT_ALGORITHM", "HS256"),
            jwt_access_token_minutes=cls._int_from_env(
                "CALMINER_JWT_ACCESS_MINUTES", 15
            ),
            jwt_refresh_token_days=cls._int_from_env(
                "CALMINER_JWT_REFRESH_DAYS", 7
            ),
            session_access_cookie_name=os.getenv(
                "CALMINER_SESSION_ACCESS_COOKIE", "calminer_access_token"
            ),
            session_refresh_cookie_name=os.getenv(
                "CALMINER_SESSION_REFRESH_COOKIE", "calminer_refresh_token"
            ),
            session_cookie_secure=cls._bool_from_env(
                "CALMINER_SESSION_COOKIE_SECURE", False
            ),
            session_cookie_domain=os.getenv("CALMINER_SESSION_COOKIE_DOMAIN"),
            session_cookie_path=os.getenv("CALMINER_SESSION_COOKIE_PATH", "/"),
            session_header_name=os.getenv(
                "CALMINER_SESSION_HEADER_NAME", "Authorization"
            ),
            session_header_prefix=os.getenv(
                "CALMINER_SESSION_HEADER_PREFIX", "Bearer"
            ),
            session_allow_header_fallback=cls._bool_from_env(
                "CALMINER_SESSION_ALLOW_HEADER_FALLBACK", True
            ),
            admin_email=os.getenv(
                "CALMINER_SEED_ADMIN_EMAIL", "admin@calminer.local"
            ),
            admin_username=os.getenv(
                "CALMINER_SEED_ADMIN_USERNAME", "admin"
            ),
            admin_password=os.getenv(
                "CALMINER_SEED_ADMIN_PASSWORD", "ChangeMe123!"
            ),
            admin_roles=cls._parse_admin_roles(
                os.getenv("CALMINER_SEED_ADMIN_ROLES")
            ),
            admin_force_reset=cls._bool_from_env(
                "CALMINER_SEED_FORCE", False
            ),
            pricing_default_payable_pct=cls._float_from_env(
                "CALMINER_PRICING_DEFAULT_PAYABLE_PCT", 100.0
            ),
            pricing_default_currency=cls._optional_str(
                "CALMINER_PRICING_DEFAULT_CURRENCY", "USD"
            ),
            pricing_moisture_threshold_pct=cls._float_from_env(
                "CALMINER_PRICING_MOISTURE_THRESHOLD_PCT", 8.0
            ),
            pricing_moisture_penalty_per_pct=cls._float_from_env(
                "CALMINER_PRICING_MOISTURE_PENALTY_PER_PCT", 0.0
            ),
        )

    @staticmethod
    def _int_from_env(name: str, default: int) -> int:
        raw_value = os.getenv(name)
        if raw_value is None:
            return default
        try:
            return int(raw_value)
        except ValueError:
            return default

    @staticmethod
    def _bool_from_env(name: str, default: bool) -> bool:
        raw_value = os.getenv(name)
        if raw_value is None:
            return default
        lowered = raw_value.strip().lower()
        if lowered in {"1", "true", "yes", "on"}:
            return True
        if lowered in {"0", "false", "no", "off"}:
            return False
        return default

    @staticmethod
    def _parse_admin_roles(raw_value: str | None) -> tuple[str, ...]:
        if not raw_value:
            return ("admin",)
        parts = [segment.strip()
                 for segment in raw_value.split(",") if segment.strip()]
        if "admin" not in parts:
            parts.insert(0, "admin")
        seen: set[str] = set()
        ordered: list[str] = []
        for role_name in parts:
            if role_name not in seen:
                ordered.append(role_name)
                seen.add(role_name)
        return tuple(ordered)

    @staticmethod
    def _float_from_env(name: str, default: float) -> float:
        raw_value = os.getenv(name)
        if raw_value is None:
            return default
        try:
            return float(raw_value)
        except ValueError:
            return default

    @staticmethod
    def _optional_str(name: str, default: str | None = None) -> str | None:
        raw_value = os.getenv(name)
        if raw_value is None or raw_value.strip() == "":
            return default
        return raw_value.strip()

    def jwt_settings(self) -> JWTSettings:
        """Build runtime JWT settings compatible with token helpers."""

        return JWTSettings(
            secret_key=self.jwt_secret_key,
            algorithm=self.jwt_algorithm,
            access_token_ttl=timedelta(minutes=self.jwt_access_token_minutes),
            refresh_token_ttl=timedelta(days=self.jwt_refresh_token_days),
        )

    def session_settings(self) -> SessionSettings:
        """Provide transport configuration for session tokens."""

        return SessionSettings(
            access_cookie_name=self.session_access_cookie_name,
            refresh_cookie_name=self.session_refresh_cookie_name,
            cookie_secure=self.session_cookie_secure,
            cookie_domain=self.session_cookie_domain,
            cookie_path=self.session_cookie_path,
            header_name=self.session_header_name,
            header_prefix=self.session_header_prefix,
            allow_header_fallback=self.session_allow_header_fallback,
        )

    def admin_bootstrap_settings(self) -> AdminBootstrapSettings:
        """Return configured admin bootstrap settings."""

        return AdminBootstrapSettings(
            email=self.admin_email,
            username=self.admin_username,
            password=self.admin_password,
            roles=self.admin_roles,
            force_reset=self.admin_force_reset,
        )

    def pricing_metadata(self) -> PricingMetadata:
        """Build pricing metadata defaults."""

        return PricingMetadata(
            default_payable_pct=self.pricing_default_payable_pct,
            default_currency=self.pricing_default_currency,
            moisture_threshold_pct=self.pricing_moisture_threshold_pct,
            moisture_penalty_per_pct=self.pricing_moisture_penalty_per_pct,
        )


@lru_cache(maxsize=1)
def get_settings() -> Settings:
    """Return cached application settings."""

    return Settings.from_environment()
