"""File injection — write context files to target directories."""

from __future__ import annotations

import json
import sys
from pathlib import Path

from syke.distribution.formatters import format_profile
from syke.models import UserProfile

CLAUDE_SETTINGS_PATH = Path.home() / ".claude" / "settings.json"
CLAUDE_DESKTOP_CONFIG_PATH = Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json"


def inject_profile(profile: UserProfile, target_dir: str, fmt: str = "claude-md") -> Path:
    """Inject a formatted profile into a target directory.

    Args:
        profile: The user profile to inject
        target_dir: Target directory path
        fmt: Format to use (claude-md or user-md)

    Returns:
        Path to the written file
    """
    target = Path(target_dir)
    target.mkdir(parents=True, exist_ok=True)

    filename = {
        "claude-md": "CLAUDE.md",
        "user-md": "USER.md",
    }.get(fmt, "CLAUDE.md")

    content = format_profile(profile, fmt)
    file_path = target / filename
    file_path.write_text(content)

    return file_path


def inject_mcp_config(user_id: str, cwd: Path | None = None) -> Path:
    """Inject Syke MCP server config into ~/.claude/settings.json.

    Merges the syke server entry into the existing mcpServers key,
    preserving any other servers the user already has configured.

    Args:
        user_id: User identifier for the MCP server.
        cwd: Working directory for the MCP process. Omitted from config when None
             (pip installs don't need it since DATA_DIR is absolute).

    Returns:
        Path to the updated settings file.
    """
    settings_path = CLAUDE_SETTINGS_PATH
    settings_path.parent.mkdir(parents=True, exist_ok=True)

    if settings_path.exists():
        settings = json.loads(settings_path.read_text())
    else:
        settings = {}

    mcp_servers = settings.setdefault("mcpServers", {})
    server_entry: dict = {
        "command": sys.executable,
        "args": ["-m", "syke", "--user", user_id, "serve", "--transport", "stdio"],
    }
    if cwd is not None:
        server_entry["cwd"] = str(cwd)
    mcp_servers["syke"] = server_entry

    settings_path.write_text(json.dumps(settings, indent=2) + "\n")
    return settings_path


def inject_mcp_config_desktop(user_id: str, cwd: Path | None = None) -> Path | None:
    """Inject Syke MCP server config into Claude Desktop (macOS only).

    Merges the syke server entry into the existing mcpServers key,
    preserving any other servers the user already has configured.

    Args:
        user_id: User identifier for the MCP server.
        cwd: Working directory for the MCP process. Omitted from config when None.

    Returns:
        Path to the updated config file, or None if not on macOS
        or Claude Desktop is not installed.
    """
    if sys.platform != "darwin":
        return None

    config_path = CLAUDE_DESKTOP_CONFIG_PATH
    if not config_path.parent.exists():
        return None  # Claude Desktop not installed

    if config_path.exists():
        config = json.loads(config_path.read_text())
    else:
        config = {}

    mcp_servers = config.setdefault("mcpServers", {})
    server_entry: dict = {
        "command": sys.executable,
        "args": ["-m", "syke", "--user", user_id, "serve", "--transport", "stdio"],
    }
    if cwd is not None:
        server_entry["cwd"] = str(cwd)
    mcp_servers["syke"] = server_entry

    config_path.write_text(json.dumps(config, indent=2) + "\n")
    return config_path


def inject_mcp_config_project(user_id: str, project_root: Path) -> Path | None:
    """Inject Syke MCP into project-level .claude/settings.json if it exists.

    If the project has its own .claude/settings.json, Claude Code uses that
    instead of the global one. This ensures the syke MCP server entry is
    present in project-level settings too.

    Uses a relative .venv path so the config is portable.

    Returns:
        Path to the updated settings file, or None if no project settings exist.
    """
    settings_path = project_root / ".claude" / "settings.json"
    if not settings_path.exists():
        return None  # No project settings — global config is sufficient

    settings = json.loads(settings_path.read_text())

    mcp_servers = settings.setdefault("mcpServers", {})
    mcp_servers["syke"] = {
        "command": ".venv/bin/python",
        "args": ["-m", "syke", "--user", user_id, "serve", "--transport", "stdio"],
    }

    settings_path.write_text(json.dumps(settings, indent=2) + "\n")
    return settings_path


def inject_hooks_config(project_root: Path) -> Path:
    """Inject Syke lifecycle hooks into ~/.claude/settings.json.

    Adds SessionStart and Stop hook entries pointing to our shell scripts.
    Merges with existing hooks — never overwrites the user's other hooks.

    Returns:
        Path to the updated settings file.
    """
    settings_path = CLAUDE_SETTINGS_PATH
    settings_path.parent.mkdir(parents=True, exist_ok=True)

    if settings_path.exists():
        settings = json.loads(settings_path.read_text())
    else:
        settings = {}

    hooks = settings.setdefault("hooks", {})

    start_script = str(project_root / ".claude" / "hooks" / "syke-session-start.sh")
    stop_script = str(project_root / ".claude" / "hooks" / "syke-session-stop.sh")

    # SessionStart — append our hook, don't overwrite existing ones
    session_start = hooks.setdefault("SessionStart", [])
    # Remove any existing syke hooks to avoid duplicates
    session_start = [h for h in session_start if "syke-session-start" not in h.get("command", "")]
    session_start.append({
        "type": "command",
        "command": f"bash {start_script}",
    })
    hooks["SessionStart"] = session_start

    # Stop — append our hook
    stop = hooks.setdefault("Stop", [])
    stop = [h for h in stop if "syke-session-stop" not in h.get("command", "")]
    stop.append({
        "type": "command",
        "command": f"bash {stop_script}",
    })
    hooks["Stop"] = stop

    settings_path.write_text(json.dumps(settings, indent=2) + "\n")
    return settings_path
