from __future__ import annotations

import os
from dataclasses import dataclass
from pathlib import Path

PROJECT_ROOT = Path(__file__).resolve().parents[2]


class ConfigurationError(ValueError):
    pass


def _resolve_path(raw_value: str, default: Path) -> Path:
    candidate = Path(raw_value).expanduser() if raw_value.strip() else default
    if not candidate.is_absolute():
        candidate = PROJECT_ROOT / candidate
    return candidate.resolve()


def _parse_port(raw_value: str) -> int:
    try:
        port = int(raw_value)
    except ValueError as exc:
        raise ConfigurationError("APP_PORT must be an integer") from exc
    if not 1 <= port <= 65535:
        raise ConfigurationError("APP_PORT must be between 1 and 65535")
    return port


@dataclass(frozen=True, slots=True)
class Settings:
    environment: str
    debug: bool
    data_directory: Path
    database_path: Path
    mentor_skills_directory: Path
    private_mentor_skills_directory: Path
    log_file: Path | None
    log_level: str
    host: str
    port: int
    encryption_key: str | None
    frontend_dist_directory: Path = PROJECT_ROOT / "frontend" / "dist"
    timezone: str = "Asia/Shanghai"

    @classmethod
    def from_environment(cls) -> Settings:
        environment = os.getenv("APP_ENV", "development").strip().lower()
        if environment not in {"development", "test", "production"}:
            raise ConfigurationError("APP_ENV must be development, test, or production")
        timezone = os.getenv("APP_TIMEZONE", "Asia/Shanghai").strip()
        if timezone != "Asia/Shanghai":
            raise ConfigurationError("APP_TIMEZONE must be Asia/Shanghai")

        data_directory = _resolve_path(os.getenv("APP_DATA_DIR", ""), PROJECT_ROOT / "data")
        database_path = _resolve_path(
            os.getenv("APP_DATABASE_PATH", ""), data_directory / "xiaobai.db"
        )
        mentor_skills_directory = _resolve_path(
            os.getenv("APP_MENTOR_SKILLS_DIR", ""), PROJECT_ROOT / "config" / "mentor-skills"
        )
        private_mentor_skills_directory = _resolve_path(
            os.getenv("APP_PRIVATE_MENTOR_SKILLS_DIR", ""),
            data_directory / "private-mentor-skills",
        )
        frontend_dist_directory = _resolve_path(
            os.getenv("APP_FRONTEND_DIST", ""), PROJECT_ROOT / "frontend" / "dist"
        )
        log_file = _resolve_path(
            os.getenv("APP_LOG_FILE", ""),
            data_directory / "logs" / "application.log",
        )
        log_level = os.getenv("APP_LOG_LEVEL", "INFO").strip().upper()
        if log_level not in {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}:
            raise ConfigurationError("APP_LOG_LEVEL is invalid")
        encryption_key = os.getenv("APP_ENCRYPTION_KEY", "").strip() or None
        if environment == "production" and encryption_key is None:
            raise ConfigurationError("APP_ENCRYPTION_KEY is required in production")
        return cls(
            environment=environment,
            debug=environment == "development",
            data_directory=data_directory,
            database_path=database_path,
            mentor_skills_directory=mentor_skills_directory,
            private_mentor_skills_directory=private_mentor_skills_directory,
            frontend_dist_directory=frontend_dist_directory,
            log_file=log_file,
            log_level=log_level,
            host=os.getenv("APP_HOST", "127.0.0.1").strip() or "127.0.0.1",
            port=_parse_port(os.getenv("APP_PORT", "8780")),
            encryption_key=encryption_key,
            timezone=timezone,
        )

    @classmethod
    def for_test(cls, data_directory: Path) -> Settings:
        root = data_directory.resolve()
        return cls(
            environment="test",
            debug=False,
            data_directory=root,
            database_path=root / "xiaobai-test.db",
            mentor_skills_directory=PROJECT_ROOT / "config" / "mentor-skills",
            private_mentor_skills_directory=root / "private-mentor-skills",
            frontend_dist_directory=root / "frontend-dist",
            log_file=None,
            log_level="CRITICAL",
            host="127.0.0.1",
            port=8780,
            encryption_key=None,
        )
