#!/usr/bin/env python3
from __future__ import annotations

import json
import os
from datetime import datetime, timezone
from pathlib import Path
from shutil import copy2
from typing import Any


def default_openclaw_config_path() -> Path:
    raw = os.environ.get("OPENCLAW_CONFIG_PATH", "~/.openclaw/openclaw.json")
    return Path(os.path.expanduser(raw)).resolve()


def openclaw_root(config_path: Path) -> Path:
    return config_path.expanduser().resolve().parent


def load_json(path: Path) -> dict[str, Any]:
    with path.open("r", encoding="utf-8") as f:
        return json.load(f)


def dump_json(path: Path, data: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)
        f.write("\n")


def load_openclaw_config(config_path: Path | None = None) -> dict[str, Any]:
    path = config_path or default_openclaw_config_path()
    if not path.is_file():
        raise FileNotFoundError(f"找不到 openclaw.json: {path}\n请确认 OpenClaw 已正确安装，或设置 OPENCLAW_CONFIG_PATH 环境变量指向正确的配置文件路径。")
    return load_json(path)


def list_agent_ids(config: dict[str, Any]) -> list[str]:
    agents = config.get("agents", {}).get("list", [])
    if not isinstance(agents, list):
        return []
    return [a["id"] for a in agents if isinstance(a, dict) and a.get("id")]


def find_agent_entry(config: dict[str, Any], agent_id: str) -> dict[str, Any] | None:
    agents = config.get("agents", {}).get("list", [])
    for agent in agents:
        if isinstance(agent, dict) and agent.get("id") == agent_id:
            return agent
    return None


def ensure_agent_entry(config: dict[str, Any], agent_id: str) -> dict[str, Any]:
    entry = find_agent_entry(config, agent_id)
    if entry is None:
        installed = list_agent_ids(config)
        if installed:
            hint = f"当前已安装的 agent: {', '.join(installed)}"
        else:
            hint = "当前 openclaw.json 中尚未安装任何 agent（agents.list 为空或不存在）。请先通过 agent-install 安装 agent。"
        raise ValueError(f"Agent '{agent_id}' 不存在。{hint}")
    return entry


def backup_config(config_path: Path, backup_root: Path | None = None) -> Path:
    if backup_root is None:
        backup_root = openclaw_root(config_path) / "backups"
    backup_root.mkdir(parents=True, exist_ok=True)
    timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    backup_path = backup_root / f"openclaw-{timestamp}.json"
    copy2(config_path, backup_path)
    _cleanup_old_backups(backup_root, keep=10)
    return backup_path


def _cleanup_old_backups(backup_root: Path, keep: int) -> None:
    backups = sorted(
        [p for p in backup_root.iterdir() if p.is_file() and p.name.startswith("openclaw-")],
        key=lambda p: p.stat().st_mtime,
    )
    if len(backups) > keep:
        for p in backups[: len(backups) - keep]:
            p.unlink()


def validate_json(data: dict[str, Any]) -> bool:
    try:
        json.loads(json.dumps(data, ensure_ascii=False))
        return True
    except (json.JSONDecodeError, TypeError):
        return False


def make_result(
    action: str,
    agent_id: str,
    field: str,
    before: Any,
    after: Any,
    changed: bool,
    backup_path: str | None = None,
    dry_run: bool = False,
    message: str = "",
) -> dict[str, Any]:
    return {
        "action": action,
        "agent_id": agent_id,
        "field": field,
        "before": before,
        "after": after,
        "changed": changed,
        "dry_run": dry_run,
        "backup": backup_path,
        "message": message,
        "timestamp": datetime.now(timezone.utc).isoformat(),
    }