"""中控统一 AI 环境变量:字段定义,读写,同步三实例."""
from __future__ import annotations

import os
import subprocess
import sys
from pathlib import Path
from typing import Any

from lib.env.env_file_lib import apply_env_updates, env_get_all, load_env_file_into_environ, read_env_lines
from lib.env.env_schema import (
    _field_type,
    _hot_reload,
    _is_sensitive,
    _mask_value,
    _restart_required,
    parse_env_example_schema,
    validate_env_updates,
)
from lib.paths import REPO_ROOT

AI_ENV_FIELDS: list[tuple[str, str, str]] = [
    ("AI_PROVIDER", "AI 提供方", "openai 或 ollama"),
    ("OPENAI_API_BASE", "API 地址", "OpenAI 兼容接口"),
    ("OPENAI_API_KEY", "API 密钥", "留空表示不修改"),
    ("OPENAI_MODEL", "云端模型", ""),
    ("OLLAMA_API", "Ollama 地址", "本地服务 URL"),
    ("AI_MODEL", "Ollama 模型", ""),
    ("AI_TIMEOUT_SECONDS", "请求超时(秒)", "默认 120"),
]

AI_ENV_KEYS = frozenset(k for k, _l, _n in AI_ENV_FIELDS)

INSTANCE_ENV_DIRS: dict[str, Path] = {
    "okx": REPO_ROOT / "crypto_monitor_okx",
    "binance": REPO_ROOT / "crypto_monitor_binance",
    "gate": REPO_ROOT / "crypto_monitor_gate",
}


def hub_env_path() -> str:
    return str(REPO_ROOT / "manual_trading_hub" / ".env")


def hub_example_path() -> str:
    return str(REPO_ROOT / "manual_trading_hub" / ".env.example")


def instance_example_path(exchange_key: str = "okx") -> str:
    ex = (exchange_key or "okx").strip().lower()
    base = INSTANCE_ENV_DIRS.get(ex, INSTANCE_ENV_DIRS["okx"])
    return str(base / ".env.example")


def _schema_field_map(example_path: str) -> dict[str, dict[str, Any]]:
    out: dict[str, dict[str, Any]] = {}
    for group in parse_env_example_schema(example_path):
        for field in group.get("fields") or []:
            out[field["key"]] = dict(field)
    return out


def _build_field(
    key: str,
    label: str,
    note: str,
    schema: dict[str, dict[str, Any]],
    values: dict[str, str],
) -> dict[str, Any]:
    meta = schema.get(key) or {}
    schema_default = meta.get("default") or ""
    val = values.get(key, "")
    if val == "" and schema_default:
        val = schema_default
    masked = _mask_value(key, val)
    ftype = meta.get("type") or _field_type(key, val or schema_default)
    return {
        "key": key,
        "label": label,
        "note": note or meta.get("note") or "",
        "default": val,
        "type": ftype,
        "sensitive": meta.get("sensitive", _is_sensitive(key)),
        "restart_required": meta.get("restart_required", _restart_required(key)),
        "hot_reload": meta.get("hot_reload", _hot_reload(key)),
        "current": masked["value"] if not _is_sensitive(key) else "",
        "masked": masked["masked"],
        "tail": masked.get("tail") or "",
        "has_value": masked["has_value"],
    }


def build_ai_env_payload(env_path: str | None = None, example_path: str | None = None) -> dict[str, Any]:
    env_path = env_path or hub_env_path()
    example_path = example_path or hub_example_path()
    schema = _schema_field_map(example_path)
    values = env_get_all(read_env_lines(env_path))
    fields = [
        _build_field(key, label, note, schema, values)
        for key, label, note in AI_ENV_FIELDS
    ]
    sync_status = ai_sync_status()
    return {
        "title": "AI 复盘",
        "fields": fields,
        "sync_status": sync_status,
    }


def ai_sync_status() -> dict[str, Any]:
    """比较 hub 与三实例 AI 键是否一致(用于 UI 提示)."""
    hub_vals = env_get_all(read_env_lines(hub_env_path()))
    per_instance: dict[str, dict[str, Any]] = {}
    all_ok = True
    for ex, inst_dir in INSTANCE_ENV_DIRS.items():
        path = str(inst_dir / ".env")
        if not os.path.isfile(path):
            per_instance[ex] = {"ok": False, "msg": "缺少 .env"}
            all_ok = False
            continue
        inst_vals = env_get_all(read_env_lines(path))
        mismatched = [
            k
            for k in AI_ENV_KEYS
            if (hub_vals.get(k) or "") != (inst_vals.get(k) or "")
        ]
        ok = not mismatched
        if not ok:
            all_ok = False
        per_instance[ex] = {
            "ok": ok,
            "mismatched_keys": mismatched,
        }
    return {"all_synced": all_ok, "instances": per_instance}


def _ai_validate_groups(example_path: str) -> list[dict[str, Any]]:
    schema = _schema_field_map(example_path)
    fields: list[dict[str, Any]] = []
    for key, _label, _note in AI_ENV_FIELDS:
        if key in schema:
            fields.append(schema[key])
        else:
            fields.append(
                {
                    "key": key,
                    "type": _field_type(key, ""),
                    "sensitive": _is_sensitive(key),
                    "restart_required": _restart_required(key),
                    "hot_reload": _hot_reload(key),
                }
            )
    return [{"title": "AI 复盘", "fields": fields}]


def validate_ai_env_updates(updates: dict[str, str], example_path: str | None = None) -> tuple[dict[str, str], list[str]]:
    example_path = example_path or hub_example_path()
    groups = _ai_validate_groups(example_path)
    filtered = {k: v for k, v in (updates or {}).items() if k in AI_ENV_KEYS}
    unknown = [k for k in (updates or {}) if k not in AI_ENV_KEYS]
    errors = [f"未知配置项: {k}" for k in unknown]
    clean, val_errors = validate_env_updates(groups, filtered)
    errors.extend(val_errors)
    return clean, errors


def apply_ai_env_to_all(updates: dict[str, str]) -> dict[str, Any]:
    """写入 hub .env 并强制同步三实例相同键."""
    clean, errors = validate_ai_env_updates(updates)
    if errors:
        return {"ok": False, "errors": errors, "changed": {}}
    if not clean:
        return {"ok": True, "changed": {}, "restart_required": False}

    changed: dict[str, list[str]] = {}
    targets = [("hub", hub_env_path())]
    for ex, inst_dir in INSTANCE_ENV_DIRS.items():
        targets.append((ex, str(inst_dir / ".env")))

    for name, path in targets:
        if not os.path.isfile(path):
            if name == "hub":
                return {"ok": False, "errors": [f"缺少 {path}"], "changed": {}}
            continue
        keys = apply_env_updates(path, clean)
        if keys:
            changed[name] = keys
            load_env_file_into_environ(path)

    return {
        "ok": True,
        "changed": changed,
        "restart_required": True,
        "errors": [],
    }


def restart_instances_then_hub_pm2() -> dict[str, Any]:
    """先重启三实例,最后重启中控(避免当前请求被中断)."""
    if not sys.platform.startswith("linux"):
        return {"ok": False, "msg": "仅 Linux 服务器支持 PM2 重启", "results": []}
    from lib.instance.instance_pm2_lib import restart_instance_pm2

    results: list[dict[str, Any]] = []
    for ex in ("okx", "binance", "gate"):
        r = restart_instance_pm2(ex)
        results.append({"exchange": ex, **r})
    hub_result = _restart_pm2_app("manual-trading-hub")
    results.append({"app": "manual-trading-hub", **hub_result})
    ok = all(r.get("ok") for r in results)
    return {"ok": ok, "results": results}


def restart_hub_and_instances_pm2() -> dict[str, Any]:
    """兼容旧调用:与 restart_instances_then_hub_pm2 相同顺序."""
    return restart_instances_then_hub_pm2()


def _restart_pm2_app(app_name: str) -> dict[str, Any]:
    try:
        proc = subprocess.run(
            ["pm2", "restart", app_name, "--update-env"],
            capture_output=True,
            text=True,
            timeout=120,
        )
        return {
            "ok": proc.returncode == 0,
            "msg": (proc.stdout or proc.stderr or "").strip()[:500],
            "returncode": proc.returncode,
        }
    except FileNotFoundError:
        return {"ok": False, "msg": "未找到 pm2 命令"}
    except subprocess.TimeoutExpired:
        return {"ok": False, "msg": "pm2 restart 超时"}
    except Exception as e:
        return {"ok": False, "msg": str(e)}
