"""
Config loading for active-claude-github-mcp.

Fails immediately at startup if github-config.json is missing,
malformed, or contains invalid values. No lazy loading.
"""

from __future__ import annotations

import json
import os
import warnings
from pathlib import Path

from pydantic import ValidationError

from .models import GithubConfig

_CONFIG_RELATIVE_PATH = ".claude/github-config.json"


class ConfigError(RuntimeError):
    """
    Raised when config cannot be loaded or validated.
    Always fatal — the server cannot operate without valid config.
    """


def load_config(project_dir: Path) -> GithubConfig:
    """
    Load and validate github-config.json from project_dir.

    Raises ConfigError immediately if:
    - project_dir does not exist
    - github-config.json is not present
    - JSON is malformed
    - Any required field is missing or invalid

    Never returns a partially-valid config.
    """
    if not project_dir.exists():
        raise ConfigError(
            f"Project directory does not exist: {project_dir}\n"
            "Ensure --project-dir points to your *-dev repo root."
        )

    config_path = project_dir / _CONFIG_RELATIVE_PATH

    if not config_path.exists():
        raise ConfigError(
            f"github-config.json not found at {config_path}\n"
            "Run bootstrap first: open Claude Code in this repo "
            "and say 'bootstrap yourself'."
        )

    try:
        raw = config_path.read_text(encoding="utf-8")
    except OSError as exc:
        raise ConfigError(
            f"Cannot read {config_path}: {exc}"
        ) from exc

    try:
        data = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise ConfigError(
            f"github-config.json is not valid JSON: {exc}\n"
            f"File: {config_path}"
        ) from exc

    try:
        config = GithubConfig.model_validate(data)
    except ValidationError as exc:
        # Surface every validation error, not just the first
        errors = "\n".join(
            f"  - {'.'.join(str(l) for l in e['loc'])}: {e['msg']}"
            for e in exc.errors()
        )
        raise ConfigError(
            f"github-config.json failed validation:\n{errors}\n"
            f"File: {config_path}\n"
            "Re-run bootstrap or manually correct the file."
        ) from exc

    _warn_deprecated_relationship_types(config, config_path)
    return config


def _warn_deprecated_relationship_types(
    config: GithubConfig, config_path: Path
) -> None:
    """Emit a DeprecationWarning for each related_repos entry still
    using the pre-1.3.0 `contribution_target` umbrella type (#98).

    Config continues to load — the type parses — but the user should
    migrate each entry to one of `sibling` / `cousin` / `neighbor` /
    `foreign` at next edit, per the precise sub-type definitions in
    §Multi-repo relations.
    """
    stale = [r.repo for r in config.related_repos if r.relationship == "contribution_target"]
    if not stale:
        return
    warnings.warn(
        "github-config.json uses deprecated relationship type "
        "'contribution_target' for "
        f"{stale!r}. Migrate to one of sibling / cousin / neighbor / "
        "foreign — see active-claude-github.md §Multi-repo relations "
        f"(#97). File: {config_path}",
        DeprecationWarning,
        stacklevel=3,
    )


def require_env(name: str) -> str:
    """
    Return the value of environment variable `name`.

    Raises ConfigError immediately if the variable is not set or empty.
    Used for ANTHROPIC_API_KEY and GITHUB_TOKEN.
    """
    value = os.environ.get(name, "").strip()
    if not value:
        raise ConfigError(
            f"Environment variable {name!r} is not set or empty.\n"
            "Ensure gh is authenticated and ANTHROPIC_API_KEY is set."
        )
    return value


def get_github_token() -> str:
    """
    Retrieve the GitHub token from the environment or gh CLI config.

    Tries in order:
    1. GITHUB_TOKEN environment variable
    2. GH_TOKEN environment variable
    3. `gh auth token` subprocess call

    Raises ConfigError if none are available.
    """
    for var in ("GITHUB_TOKEN", "GH_TOKEN"):
        token = os.environ.get(var, "").strip()
        if token:
            return token

    # Try gh CLI
    import subprocess  # noqa: PLC0415

    try:
        result = subprocess.run(
            ["gh", "auth", "token"],
            capture_output=True,
            text=True,
            timeout=10,
            check=False,
        )
        token = result.stdout.strip()
        if token:
            return token
        raise ConfigError(
            "gh auth token returned empty output.\n"
            "Run: gh auth login"
        )
    except FileNotFoundError as exc:
        raise ConfigError(
            "gh CLI not found. Install from https://cli.github.com "
            "and run: gh auth login"
        ) from exc
    except subprocess.TimeoutExpired as exc:
        raise ConfigError("gh auth token timed out after 10s.") from exc
