"""
Environment-aware settings for Code of Conquest.

- Loads environment variables from OS and `.env` (OS wins).
- Provides repo-relative default paths for data storage.
- Validates a few key fields (env, model backend).
- Ensures important directories exist on first load.
- Exposes a tiny singleton: get_settings().

Style:
- Python 3.11+
- Dataclasses (no Pydantic)
- Docstrings + inline comments
"""

from __future__ import annotations

import os
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import Optional

from dotenv import load_dotenv

class Environment(str, Enum):
    DEV = "dev"
    TEST = "test"
    PROD = "prod"

def _repo_root_from_here() -> Path:
    """
    Resolve the repository root by walking up from this file.

    This file lives at: project/app/core/utils/settings.py
    So parents[3] should be the repo root:
      parents[0] = utils
      parents[1] = core
      parents[2] = app
      parents[3] = project root
    """
    here = Path(__file__).resolve()
    repo_root = here.parents[3]
    return repo_root


@dataclass
class Settings:
    """
    Settings container for Code of Conquest.

    Load order:
      1) OS environment
      2) .env file (at repo root)
      3) Defaults below

    Paths default into the repo under ./data unless overridden.
    """

    # --- Core Tunables---
    env: Environment = Environment.DEV
    log_level: str = "INFO"
    flask_secret_key: str = os.getenv("FLASK_SECRET_KEY","change-me-for-prod")
    
    # APPWRITE Things
    appwrite_endpoint: str = os.getenv("APPWRITE_ENDPOINT","NOT SET")
    appwrite_project_id: str = os.getenv("APPWRITE_PROJECT_ID","NOT SET")
    appwrite_api_key: str = os.getenv("APPWRITE_API_KEY","NOT SET")

    app_name: str = "Code of Conquest"
    app_version: str = "v 0.0.1"

    # --- Paths (default under ./data) ---
    repo_root: Path = field(default_factory=_repo_root_from_here)

    # --- Build paths for convenience (not env-controlled directly) ---

    def __post_init__(self) -> None:
        # Basic validation
        if self.env not in (Environment.DEV, Environment.TEST, Environment.PROD):
            raise ValueError(f"Invalid COC_ENV: {self.env}")
        
    @staticmethod
    def _ensure_dir(path: Path) -> None:
        if path is None:
            return
        if not path.exists():
            path.mkdir(parents=True, exist_ok=True)


# ---- Singleton loader ----
_settings_singleton: Optional[Settings] = None


def get_settings() -> Settings:
    """
    Load settings from environment and `.env` once, then reuse.
    OS env always takes precedence over `.env`.

    Returns:
        Settings: A process-wide singleton instance.
    """
    global _settings_singleton
    if _settings_singleton is not None:
        return _settings_singleton

    # Load .env from repo root
    repo_root = _repo_root_from_here()
    dotenv_path = repo_root / ".env"
    load_dotenv(dotenv_path=dotenv_path, override=False)

    # Environment
    env_str = os.getenv("COC_ENV", "dev").strip().lower()
    if env_str == "dev":
        env_val = Environment.DEV
    elif env_str == "test":
        env_val = Environment.TEST
    elif env_str == "prod":
        env_val = Environment.PROD
    else:
        raise ValueError(f"COC_ENV must be one of dev|test|prod, got '{env_str}'")
    
    # Construct settings
    _settings_singleton = Settings(
        env=env_val,
        log_level=os.getenv("LOG_LEVEL", "INFO").strip().upper(),
    )

    return _settings_singleton