"""
config_loader - 参数治理双通道加载器

职责：
  1. effective_params.json — 当前系统生效值（优先读取）
  2. suggested_params.json — Hyperopt 建议值（回退读取）

优先级：effective > suggested > 策略默认值

用法：
    from config_loader import load_effective_params, merge_params
    params = load_effective_params(project_root)
"""
import json
from pathlib import Path
from typing import Any, Dict, Optional

import config_manager


def load_json(path: Path) -> Dict[str, Any]:
    if not path.exists():
        return {}
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return {}


def _effective_path(root: Path) -> Path:
    """Return the effective params path, preferring logs/ then data/offline/."""
    logs_path = root / "logs" / "effective_params.json"
    offline_path = root / "data" / "offline" / "effective_params.json"
    # Prefer logs/ (server-synced), fall back to data/offline/ (local training)
    if logs_path.exists():
        return logs_path
    if offline_path.exists():
        return offline_path
    return logs_path  # default write target


def _suggested_path(root: Path) -> Path:
    return root / "config" / "params_suggestion.json"


def load_effective_params(root: Path) -> Dict[str, Any]:
    """
    加载当前生效参数。
    优先读 effective_params.json，不存在则回退到 suggested_params.json。
    如果都没有，返回空 dict。
    """
    eff = _effective_path(root)
    sug = _suggested_path(root)

    if eff.exists():
        return config_manager.normalize_effective_runtime_payload(load_json(eff))

    if sug.exists():
        return load_json(sug)

    return {}


def get_effective_value(
    root: Path,
    param_name: str,
    default: Any = None,
) -> Any:
    """
    获取单个参数值。
    优先级：effective > suggested > default
    """
    data = load_effective_params(root)
    suggestions = data.get("suggestions", [])

    for sug in suggestions:
        if sug.get("param") == param_name and sug.get("auto_apply"):
            return sug.get("suggested_value", default)
    return default


def _atomic_write_effective_params(root: Path, content: dict, writer_name: str = "unknown") -> Path:
    """
    Atomic write with lock to prevent contention among multiple writers:
      - offline_train.py (batch training)
      - strategy_sync.py (server sync, every ~300s)
      - gui_backend.py (manual user changes)
      - config_loader.py (approved suggestions)
      - symbol_calibrator.py (calibrated values)

    Uses a .lock file with timeout to avoid deadlocks.
    """
    import time as _time
    eff = _effective_path(root)
    eff.parent.mkdir(parents=True, exist_ok=True)
    lock_path = eff.with_suffix(eff.suffix + ".lock")
    timeout_sec = 10.0
    start = _time.monotonic()

    while lock_path.exists():
        if _time.monotonic() - start > timeout_sec:
            # Stale lock — break it and proceed
            try:
                lock_path.unlink(missing_ok=True)
            except OSError:
                pass
            break
        _time.sleep(0.05)  # 50ms backoff

    try:
        lock_path.write_text(writer_name, encoding="utf-8")
        content.setdefault("timestamp", _now_iso())
        content.setdefault("source", writer_name)
        tmp = eff.with_suffix(eff.suffix + ".tmp")
        tmp.write_text(json.dumps(content, ensure_ascii=False, indent=2), encoding="utf-8")
        tmp.replace(eff)
    finally:
        try:
            lock_path.unlink(missing_ok=True)
        except OSError:
            pass
    return eff


def build_effective_params_file(root: Path, approved_suggestions: list) -> Path:
    """
    将审核通过的 suggestions 写入 effective_params.json。
    """
    content = {
        "approved_count": len(approved_suggestions),
        "suggestions": approved_suggestions,
    }
    return _atomic_write_effective_params(root, content, writer_name="user_approved")


def _now_iso() -> str:
    from datetime import datetime, timezone
    return datetime.now(timezone.utc).isoformat()


def diff_effective_vs_suggested(root: Path) -> Dict[str, Any]:
    """
    对比 effective 和 suggested 的差异，返回变更列表。
    供 GUI 显示"哪些参数待审核"。
    """
    eff_data = load_json(_effective_path(root))
    sug_data = load_json(_suggested_path(root))

    eff_params = {s["param"]: s for s in eff_data.get("suggestions", [])}
    sug_params = {s["param"]: s for s in sug_data.get("suggestions", [])}

    all_params = set(eff_params.keys()) | set(sug_params.keys())
    diffs = []

    for param in all_params:
        eff_val = eff_params.get(param, {}).get("suggested_value")
        sug_val = sug_params.get(param, {}).get("suggested_value")
        if eff_val != sug_val:
            diffs.append({
                "param": param,
                "effective_value": eff_val,
                "suggested_value": sug_val,
                "suggested_reason": sug_params.get(param, {}).get("reason", ""),
            })

    return {
        "effective_timestamp": eff_data.get("timestamp"),
        "suggested_timestamp": sug_data.get("timestamp"),
        "diff_count": len(diffs),
        "diffs": diffs,
    }
