"""Configuration loader: config.json + environment variables + TSUMUGI.md search."""

from __future__ import annotations

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


CONFIG_DIR = Path.home() / ".tsumugi"
CONFIG_FILE = CONFIG_DIR / "config.json"
PROJECTS_DIR = CONFIG_DIR / "projects"
MEMORY_FILENAME = "MEMORY.md"
INSTRUCTION_FILENAME = "TSUMUGI.md"


def _project_id(cwd: str | Path) -> str:
    """Derive a project ID from a working directory path.

    Replaces path separators and colons with dashes.
    e.g. C:\\Users\\space\\Desktop\\Tsumugi -> C--Users-space-Desktop-Tsumugi
    """
    path_str = str(Path(cwd).resolve())
    # Remove trailing separator
    path_str = path_str.rstrip("/\\")
    # Replace separators and colon
    for ch in (":\\", "\\", "/", ":"):
        path_str = path_str.replace(ch, "-")
    return path_str


@dataclass
class ProviderConfig:
    """Configuration for a single provider."""
    api_key: str = ""
    model: str = ""
    base_url: str | None = None


@dataclass
class Config:
    """Runtime configuration assembled from all sources."""

    provider: str = "anthropic"
    model: str = "claude-sonnet-4-20250514"
    api_key: str = ""
    base_url: str | None = None
    max_tokens: int = 8192
    user_name: str = "user"
    working_dir: str = field(default_factory=lambda: os.getcwd())
    instructions: list[str] = field(default_factory=list)
    memory: str = ""  # Contents of MEMORY.md
    memory_path: Path | None = None  # Path to MEMORY.md for write-back
    # Provider-specific configs for switching
    providers: dict[str, ProviderConfig] = field(default_factory=dict)


def load_config_file() -> dict:
    """Load ~/.tsumugi/config.json if it exists."""
    if CONFIG_FILE.exists():
        with open(CONFIG_FILE, encoding="utf-8") as f:
            return json.load(f)
    return {}


def find_instruction_files(start_dir: str | Path) -> list[tuple[Path, str]]:
    """Walk from start_dir upward, collecting TSUMUGI.md files.

    Returns list of (path, content) from root → leaf order so more
    specific instructions come last.
    """
    results: list[tuple[Path, str]] = []
    current = Path(start_dir).resolve()

    while True:
        candidate = current / INSTRUCTION_FILENAME
        if candidate.is_file():
            try:
                content = candidate.read_text(encoding="utf-8")
                results.append((candidate, content))
            except OSError:
                pass
        parent = current.parent
        if parent == current:
            break
        current = parent

    # reverse: root first, CWD last
    results.reverse()
    return results


def find_memory_file(cwd: str | Path) -> tuple[Path, str] | None:
    """Find and read project-specific MEMORY.md.

    Looks in ~/.tsumugi/projects/<project-id>/memory/MEMORY.md
    Returns (path, content) or None if not found.
    """
    project_dir = PROJECTS_DIR / _project_id(cwd) / "memory"
    memory_file = project_dir / MEMORY_FILENAME
    if memory_file.is_file():
        try:
            content = memory_file.read_text(encoding="utf-8")
            return (memory_file, content)
        except OSError:
            pass
    return None


def get_memory_path(cwd: str | Path) -> Path:
    """Get the path where MEMORY.md should be stored for this project."""
    return PROJECTS_DIR / _project_id(cwd) / "memory" / MEMORY_FILENAME


def build_config(
    cli_provider: str | None = None,
    cli_model: str | None = None,
    cli_api_key: str | None = None,
    cli_base_url: str | None = None,
    cli_max_tokens: int | None = None,
) -> Config:
    """Build config with priority: config.json < env vars < CLI args."""
    file_cfg = load_config_file()

    provider = cli_provider or os.environ.get(
        "TSUMUGI_PROVIDER", file_cfg.get("provider", "anthropic")
    )
    model = cli_model or os.environ.get(
        "TSUMUGI_MODEL", file_cfg.get("model", "claude-sonnet-4-20250514")
    )

    # API key: CLI > env > config file (provider-specific key > generic key)
    if cli_api_key:
        api_key = cli_api_key
    elif provider == "anthropic":
        api_key = os.environ.get(
            "ANTHROPIC_API_KEY",
            file_cfg.get("anthropic_api_key", file_cfg.get("api_key", "")),
        )
    else:
        api_key = os.environ.get(
            "OPENAI_API_KEY",
            file_cfg.get("openai_api_key", file_cfg.get("api_key", "")),
        )

    base_url = cli_base_url or os.environ.get(
        "TSUMUGI_BASE_URL", file_cfg.get("base_url")
    )
    max_tokens = cli_max_tokens or int(
        os.environ.get("TSUMUGI_MAX_TOKENS", file_cfg.get("max_tokens", 8192))
    )
    user_name = os.environ.get(
        "USER_NAME", file_cfg.get("user_name", "user")
    )

    cwd = os.getcwd()
    instruction_files = find_instruction_files(cwd)
    instructions = [content for _, content in instruction_files]

    # Load project-specific MEMORY.md
    memory_result = find_memory_file(cwd)
    memory = memory_result[1] if memory_result else ""
    memory_path = memory_result[0] if memory_result else get_memory_path(cwd)

    # Collect all available provider configs for switching
    providers: dict[str, ProviderConfig] = {}

    anthropic_key = cli_api_key if provider == "anthropic" and cli_api_key else (
        os.environ.get("ANTHROPIC_API_KEY", file_cfg.get("anthropic_api_key", ""))
    )
    if anthropic_key:
        providers["anthropic"] = ProviderConfig(
            api_key=anthropic_key,
            model=file_cfg.get("anthropic_model", "claude-sonnet-4-20250514"),
        )

    openai_key = cli_api_key if provider == "openai" and cli_api_key else (
        os.environ.get("OPENAI_API_KEY", file_cfg.get("openai_api_key", ""))
    )
    if openai_key:
        openai_base = os.environ.get(
            "OPENAI_BASE_URL", file_cfg.get("openai_base_url")
        )
        providers["openai"] = ProviderConfig(
            api_key=openai_key,
            model=file_cfg.get("openai_model", "gpt-4o"),
            base_url=openai_base,
        )

    # Also store the current provider's key in providers
    if provider not in providers and api_key:
        providers[provider] = ProviderConfig(
            api_key=api_key, model=model, base_url=base_url
        )

    return Config(
        provider=provider,
        model=model,
        api_key=api_key,
        base_url=base_url,
        max_tokens=max_tokens,
        user_name=user_name,
        working_dir=cwd,
        instructions=instructions,
        memory=memory,
        memory_path=memory_path,
        providers=providers,
    )
