"""
Claude Desktop (및 기타 MCP 클라이언트) 설정 파일에
llm4s proxy를 자동으로 주입/복원하는 모듈.

흐름:
  inject  → config 백업 → mcpServers 각 항목을 llm4s proxy로 래핑 → 원자적 저장
  restore → 가장 최근 백업으로 원자적 복원
"""

import json
import os
import platform
import shutil
from datetime import datetime
from pathlib import Path
from typing import Optional

from app.core.config import atomic_write_json, APP_NAME

# 백업 저장 디렉토리
from platformdirs import user_data_dir
BACKUP_DIR = Path(user_data_dir(APP_NAME)) / "backups"


# ── 플랫폼별 Claude Desktop 설정 경로 ──────────────────────────────────────

def get_claude_config_path() -> Optional[Path]:
    """플랫폼에 따라 Claude Desktop claude_desktop_config.json 경로 반환."""
    system = platform.system()
    if system == "Darwin":
        p = Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"
    elif system == "Windows":
        appdata = Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
        p = appdata / "Claude" / "claude_desktop_config.json"
    elif system == "Linux":
        xdg = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config"))
        p = xdg / "Claude" / "claude_desktop_config.json"
        if not p.exists():
            flatpak = Path.home() / ".var" / "app" / "io.claude.Claude" / "config" / "claude_desktop_config.json"
            if flatpak.exists():
                return flatpak
    else:
        return None
    return p


# ── 이중 래핑 감지 ──────────────────────────────────────────────────────────

def is_already_proxied(entry: dict) -> bool:
    """MCP 서버 항목이 이미 llm4s proxy로 감싸져 있으면 True."""
    command = entry.get("command", "")
    args = entry.get("args") or []

    # 1) command 이름이 llm4s (절대경로 포함)
    if Path(command).name == "llm4s":
        return True

    # 2) python -m app.cli proxy 형태
    if Path(command).name in ("python", "python3") and "-m" in args:
        idx = args.index("-m")
        if idx + 1 < len(args) and "llm4s" in args[idx + 1]:
            return True

    # 3) shutil.which 로 실제 바이너리 경로 비교
    llm4s_bin = shutil.which("llm4s")
    if llm4s_bin:
        try:
            if Path(command).resolve() == Path(llm4s_bin).resolve():
                return True
        except Exception:
            pass

    return False


# ── 백업 / 복원 ─────────────────────────────────────────────────────────────

def _backup_config(config_path: Path) -> Path:
    """config_path 를 타임스탬프 기반 파일명으로 BACKUP_DIR 에 복사."""
    BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    ts = datetime.now().strftime("%Y%m%dT%H%M%S")
    backup_path = BACKUP_DIR / f"{config_path.stem}_{ts}.json"
    shutil.copy2(config_path, backup_path)
    # 백업 20개 초과 시 오래된 것 삭제
    backups = sorted(BACKUP_DIR.glob(f"{config_path.stem}_*.json"))
    for old in backups[:-20]:
        old.unlink(missing_ok=True)
    return backup_path


def _find_latest_backup(config_path: Path) -> Optional[Path]:
    """config_path 에 대한 가장 최근 백업 파일 반환."""
    backups = sorted(BACKUP_DIR.glob(f"{config_path.stem}_*.json"))
    return backups[-1] if backups else None


# ── 핵심 inject / restore ───────────────────────────────────────────────────

def inject_all_servers(dry_run: bool = False) -> dict:
    """
    Claude Desktop 설정의 mcpServers 를 llm4s proxy 로 래핑합니다.

    Returns:
        {
            "config_path": Path,
            "wrapped":     [서버명, ...],   # 새로 래핑된 서버
            "skipped":     [서버명, ...],   # 이미 래핑된 서버 (건너뜀)
            "backup_path": Path | None,
            "dry_run":     bool,
            "preview":     dict | None,     # dry_run=True 일 때 변경 후 mcpServers
        }
    """
    config_path = get_claude_config_path()
    if not config_path or not config_path.exists():
        raise FileNotFoundError(
            f"Claude Desktop 설정 파일을 찾을 수 없습니다.\n"
            f"예상 경로: {config_path}\n"
            "Claude Desktop 이 설치되어 있는지 확인하세요."
        )

    llm4s_bin = shutil.which("llm4s")
    if not llm4s_bin:
        raise RuntimeError(
            "llm4s 바이너리를 PATH 에서 찾을 수 없습니다.\n"
            "pip install -e . 또는 pip install llm4s 로 재설치하세요."
        )

    config = json.loads(config_path.read_text(encoding="utf-8"))
    servers: dict = config.get("mcpServers", {})

    wrapped, skipped = [], []
    new_servers: dict = {}

    for name, entry in servers.items():
        if is_already_proxied(entry):
            skipped.append(name)
            new_servers[name] = entry
        else:
            orig_cmd  = entry.get("command", "")
            orig_args = entry.get("args") or []

            # node/python 같은 상대 명령은 절대경로로 해석해 PATH 의존성 제거
            resolved = shutil.which(orig_cmd)
            cmd_to_use = resolved if resolved else orig_cmd

            injected_env = dict(entry.get("env") or {})
            injected_env["LLM4S_PROJECT_NAME"] = name  # TUI 프로젝트 필터용

            new_entry: dict = {
                "command": llm4s_bin,
                "args": ["proxy", "--name", name, "--", cmd_to_use] + orig_args,
                "env": injected_env,
            }

            new_servers[name] = new_entry
            wrapped.append(name)

    result: dict = {
        "config_path": config_path,
        "wrapped":     wrapped,
        "skipped":     skipped,
        "backup_path": None,
        "dry_run":     dry_run,
        "preview":     new_servers if dry_run else None,
    }

    if dry_run or not wrapped:
        return result

    backup_path = _backup_config(config_path)
    config["mcpServers"] = new_servers
    atomic_write_json(config_path, config)

    result["backup_path"] = backup_path
    return result


def restore_all_servers() -> dict:
    """가장 최근 백업으로 Claude Desktop 설정을 복원합니다."""
    config_path = get_claude_config_path()
    if not config_path:
        raise FileNotFoundError("Claude Desktop 설정 파일 경로를 확인할 수 없습니다.")

    backup_path = _find_latest_backup(config_path)
    if not backup_path:
        raise FileNotFoundError(
            f"복원할 백업 파일이 없습니다.\n백업 디렉토리: {BACKUP_DIR}"
        )

    data = json.loads(backup_path.read_text(encoding="utf-8"))
    atomic_write_json(config_path, data)

    return {"config_path": config_path, "backup_path": backup_path}
