"""Configuration module for MixMail.

Loads environment variables from sensible locations for both local
development and installed CLI usage.
"""

from __future__ import annotations

import os
import shutil
from dataclasses import dataclass, field
from pathlib import Path

from dotenv import load_dotenv

_PACKAGE_ROOT = Path(__file__).resolve().parent.parent
_APP_HOME = Path(
    os.getenv(
        "MIXMAIL_HOME",
        os.getenv("MICROCLAWD_HOME", str(Path.home() / ".mixmail")),
    )
).expanduser()
_LEGACY_APP_HOME = Path.home() / ".microclawd"
_DEFAULT_CREDENTIALS_FILENAME = "client_secret.json"
_DEFAULT_TOKEN_FILENAME = "token.json"
_LEGACY_PROJECT_CREDENTIALS_FILENAME = (
    "client_secret_154133424340-hge5f1tr3038dvr6llk8mdp9ibrh7jul."
    "apps.googleusercontent.com.json"
)


def _load_env_files() -> None:
    """Load .env files from preferred config locations.

    Precedence is determined by load order because ``override=False`` is the
    default in ``load_dotenv``. Earlier files win.
    """
    candidates = [
        Path.cwd() / ".env",
        _APP_HOME / ".env",
        _LEGACY_APP_HOME / ".env",
        _PACKAGE_ROOT / ".env",
    ]
    for candidate in candidates:
        if candidate.exists():
            load_dotenv(candidate)


def _resolve_env_path() -> Path:
    """Return the preferred writable .env path."""
    cwd_env = Path.cwd() / ".env"
    if cwd_env.exists():
        return cwd_env
    return _APP_HOME / ".env"


def _resolve_config_path(path_value: str, *, must_exist: bool) -> Path:
    """Resolve config file locations for both dev and installed CLI usage."""
    raw_path = Path(path_value).expanduser()
    if raw_path.is_absolute():
        return raw_path

    candidates = [
        Path.cwd() / raw_path,
        _APP_HOME / raw_path,
        _LEGACY_APP_HOME / raw_path,
        _PACKAGE_ROOT / raw_path,
    ]

    if must_exist:
        for candidate in candidates:
            if candidate.exists():
                return candidate

    return _APP_HOME / raw_path


_load_env_files()


@dataclass(frozen=True)
class Settings:
    """Immutable application-wide settings loaded from environment variables."""

    # --- LLM ------------------------------------------------------------
    llm_base_url: str = field(
        default_factory=lambda: os.getenv(
            "LLM_BASE_URL", "http://localhost:1200/v1"
        ).rstrip("/")
    )
    llm_model: str = field(
        default_factory=lambda: os.getenv(
            "LLM_MODEL", "qwen2.5-coder-1.5b-instruct"
        )
    )

    # --- Gmail ----------------------------------------------------------
    gmail_credentials_path: Path = field(
        default_factory=lambda: _resolve_config_path(
            os.getenv(
                "GMAIL_CREDENTIALS_PATH",
                _DEFAULT_CREDENTIALS_FILENAME,
            ),
            must_exist=True,
        )
    )
    gmail_token_path: Path = field(
        default_factory=lambda: _resolve_config_path(
            os.getenv("GMAIL_TOKEN_PATH", "token.json"),
            must_exist=False,
        )
    )

    # --- Pagination -----------------------------------------------------
    max_results: int = field(
        default_factory=lambda: int(os.getenv("MAX_RESULTS", "10"))
    )

    # --- System prompt for the agent ------------------------------------
    system_prompt: str = field(default_factory=lambda: os.getenv(
        "SYSTEM_PROMPT",
        (
            "You are MixMail, an intelligent email assistant. You help the user search, "
            "read, send, and summarize their Gmail inbox using natural language. "
            "Translate user queries into Gmail search syntax and use the "
            "provided tools to fetch results. Always present results in a "
            "clean, numbered format with sender, subject, date, and a brief "
            "summary. If the user asks for a summary of a thread, fetch the "
            "full thread and provide a concise recap. Detect and list "
            "attachments when present. Only send email when the user has "
            "explicitly asked to send it and the recipient, subject, and body are clear."
        ),
    ))

    def validate(self) -> None:
        """Raise ``ValueError`` if critical settings are missing."""
        if not self.gmail_credentials_path.exists():
            raise ValueError(
                f"Gmail credentials file not found at "
                f"{self.gmail_credentials_path}. "
                f"Download it from the Google Cloud Console and place it in "
                f"{_APP_HOME} or point GMAIL_CREDENTIALS_PATH to it."
            )


def get_settings() -> Settings:
    """Return a fresh settings snapshot from the current environment."""
    _load_env_files()
    if (
        "GMAIL_CREDENTIALS_PATH" not in os.environ
        and (_PACKAGE_ROOT / _LEGACY_PROJECT_CREDENTIALS_FILENAME).exists()
    ):
        os.environ["GMAIL_CREDENTIALS_PATH"] = _LEGACY_PROJECT_CREDENTIALS_FILENAME
    return Settings()


def _ensure_parent_dir(path: Path) -> None:
    """Create parent directories for a path if needed."""
    path.parent.mkdir(parents=True, exist_ok=True)


def _normalise_llm_base_url(base_url: str) -> str:
    """Normalise user-provided base URLs for OpenAI-compatible servers."""
    cleaned = base_url.strip().rstrip("/")
    lowered = cleaned.lower()

    if not lowered.startswith(("http://", "https://")):
        raise ValueError(
            "Local LLM URL must start with http:// or https://."
        )

    if cleaned.endswith("/chat/completions"):
        cleaned = cleaned[: -len("/chat/completions")]
        lowered = cleaned.lower()

    if "/chat/" in lowered:
        raise ValueError(
            "Use the base URL (for example http://localhost:1200/v1), "
            "not a nested /chat/* path."
        )

    if lowered.endswith("/chat/completion") or lowered.endswith("/chat/completiona"):
        raise ValueError(
            "Did you mean /v1 or /v1/chat/completions?"
        )

    if not cleaned:
        raise ValueError("Local LLM URL cannot be empty.")
    return cleaned


def save_llm_base_url(base_url: str) -> Path:
    """Persist the local LLM base URL to the preferred .env file."""
    normalised_base_url = _normalise_llm_base_url(base_url)
    env_path = _resolve_env_path()
    _ensure_parent_dir(env_path)

    lines: list[str] = []
    if env_path.exists():
        lines = env_path.read_text(encoding="utf-8").splitlines()

    updated = False
    new_lines: list[str] = []
    for line in lines:
        if line.startswith("LLM_BASE_URL="):
            new_lines.append(f"LLM_BASE_URL={normalised_base_url}")
            updated = True
        else:
            new_lines.append(line)

    if not updated:
        if new_lines and new_lines[-1].strip():
            new_lines.append("")
        new_lines.append(f"LLM_BASE_URL={normalised_base_url}")

    env_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
    os.environ["LLM_BASE_URL"] = normalised_base_url
    return env_path


def save_mail_credentials(credentials_text: str) -> Path:
    """Persist pasted Google OAuth credentials JSON to the config location."""
    destination = _APP_HOME / _DEFAULT_CREDENTIALS_FILENAME
    _ensure_parent_dir(destination)
    destination.write_text(credentials_text.strip() + "\n", encoding="utf-8")

    env_path = _resolve_env_path()
    _ensure_parent_dir(env_path)
    lines: list[str] = []
    if env_path.exists():
        lines = env_path.read_text(encoding="utf-8").splitlines()

    updates = {
        "GMAIL_CREDENTIALS_PATH": _DEFAULT_CREDENTIALS_FILENAME,
        "GMAIL_TOKEN_PATH": _DEFAULT_TOKEN_FILENAME,
    }
    seen: set[str] = set()
    new_lines: list[str] = []
    for line in lines:
        replaced = False
        for key, value in updates.items():
            if line.startswith(f"{key}="):
                new_lines.append(f"{key}={value}")
                seen.add(key)
                replaced = True
                break
        if not replaced:
            new_lines.append(line)

    for key, value in updates.items():
        if key not in seen:
            if new_lines and new_lines[-1].strip():
                new_lines.append("")
            new_lines.append(f"{key}={value}")

    env_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
    os.environ["GMAIL_CREDENTIALS_PATH"] = _DEFAULT_CREDENTIALS_FILENAME
    os.environ["GMAIL_TOKEN_PATH"] = _DEFAULT_TOKEN_FILENAME
    return destination


def get_setup_paths() -> dict[str, Path | str]:
    """Return the main config paths MixMail uses for setup."""
    settings = get_settings()
    return {
        "app_home": _APP_HOME,
        "env_path": _resolve_env_path(),
        "llm_base_url": settings.llm_base_url,
        "credentials_path": settings.gmail_credentials_path,
        "token_path": settings.gmail_token_path,
    }


def import_mail_credentials_file(source_path: str) -> Path:
    """Copy a Google OAuth credentials file into the MixMail config area."""
    source = Path(source_path).expanduser()
    if not source.exists():
        raise FileNotFoundError(f"File not found: {source}")

    destination = _APP_HOME / _DEFAULT_CREDENTIALS_FILENAME
    _ensure_parent_dir(destination)
    shutil.copyfile(source, destination)

    env_path = _resolve_env_path()
    _ensure_parent_dir(env_path)
    lines: list[str] = []
    if env_path.exists():
        lines = env_path.read_text(encoding="utf-8").splitlines()

    updates = {
        "GMAIL_CREDENTIALS_PATH": _DEFAULT_CREDENTIALS_FILENAME,
        "GMAIL_TOKEN_PATH": _DEFAULT_TOKEN_FILENAME,
    }
    seen: set[str] = set()
    new_lines: list[str] = []
    for line in lines:
        replaced = False
        for key, value in updates.items():
            if line.startswith(f"{key}="):
                new_lines.append(f"{key}={value}")
                seen.add(key)
                replaced = True
                break
        if not replaced:
            new_lines.append(line)

    for key, value in updates.items():
        if key not in seen:
            if new_lines and new_lines[-1].strip():
                new_lines.append("")
            new_lines.append(f"{key}={value}")

    env_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
    os.environ["GMAIL_CREDENTIALS_PATH"] = _DEFAULT_CREDENTIALS_FILENAME
    os.environ["GMAIL_TOKEN_PATH"] = _DEFAULT_TOKEN_FILENAME
    return destination


# ---------------------------------------------------------------------------
# Singleton settings instance
# ---------------------------------------------------------------------------
settings = Settings()
