"""Parameter flow management for suggestion -> shadow -> effective -> rollback."""

from __future__ import annotations

import json
import time
from pathlib import Path
from typing import Any, Dict, Optional

import config_manager

SUGGESTION_FILE: Path = config_manager.get_config_dir() / "params_suggestion.json"
LEGACY_SUGGESTION_FILE: Path = config_manager.get_logs_dir() / "params_suggestion.json"
SHADOW_FILE: Path = config_manager.get_config_dir() / "params_shadow.json"
EFFECTIVE_FILE: Path = config_manager.effective_config_path()
ARCHIVE_DIR: Path = config_manager.get_config_dir() / "params_archive"
ROLLBACK_FILE: Path = config_manager.get_logs_dir() / "effective_params_rollback.json"
HISTORY_FILE: Path = config_manager.get_logs_dir() / "effective_params_history.ndjson"


def _now_iso() -> str:
    return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())


def _read_json(path: Path) -> Dict[str, Any]:
    if not path.exists():
        return {}


def _resolve_suggestion_file(explicit: Optional[Path] = None) -> Path:
    if explicit is not None:
        return Path(explicit)
    if SUGGESTION_FILE.exists():
        return SUGGESTION_FILE
    if LEGACY_SUGGESTION_FILE.exists():
        return LEGACY_SUGGESTION_FILE
    return SUGGESTION_FILE
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return {}


def _append_history(event_type: str, payload: Dict[str, Any]) -> None:
    HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True)
    with open(HISTORY_FILE, "a", encoding="utf-8") as f:
        f.write(json.dumps({"event_type": event_type, "timestamp": _now_iso(), **payload}, ensure_ascii=False) + "\n")


def _suggestions_from_param_map(params: Dict[str, Any], *, source: str, applied: bool, auto_apply: bool) -> list[dict[str, Any]]:
    return [
        {
            "id": str(source or "PARAM_FLOW").upper(),
            "param": str(param),
            "applied_value": value,
            "suggested_value": value,
            "applied": applied,
            "auto_apply": auto_apply,
            "source": source,
        }
        for param, value in params.items()
    ]


def extract_param_map(payload: Dict[str, Any]) -> Dict[str, Any]:
    """Extract a flat parameter map from a structured payload."""
    normalized = config_manager.normalize_effective_runtime_payload(payload)
    out: Dict[str, Any] = {}
    params = normalized.get("params", {})
    if isinstance(params, dict):
        out.update({str(k): v for k, v in params.items()})
    suggestions = normalized.get("suggestions", [])
    if isinstance(suggestions, list):
        for s in suggestions:
            if isinstance(s, dict) and "param" in s:
                value = s.get("applied_value")
                if value is None:
                    value = s.get("suggested_value")
                out[str(s["param"])] = value
    return out


def load_runtime_param_payload() -> Dict[str, Any]:
    """Load the canonical runtime parameter payload from effective params."""
    payload = config_manager.load_effective_runtime_config()
    if not payload:
        return {}
    params = extract_param_map(payload)
    payload["params"] = params
    payload.setdefault("suggestions", [])
    payload["_source_file"] = str(config_manager.effective_config_path())
    return payload


def get_effective_rollback_state() -> Dict[str, Any]:
    """Return the rollback state for current effective params."""
    rollback = _read_json(ROLLBACK_FILE)
    if not rollback:
        return {"available": False, "reason": "no previous snapshot"}
    return {
        "available": True,
        "source": rollback.get("source", "unknown"),
        "created_at": rollback.get("created_at", ""),
        "reason": rollback.get("reason", ""),
        "has_payload": bool(isinstance(rollback.get("effective_payload"), dict) and rollback.get("effective_payload")),
    }


def complete_effective_review(*, source: str = "manual_review") -> Dict[str, Any]:
    """Mark the effective params review as complete."""
    payload = config_manager.load_effective_runtime_config()
    if not payload:
        raise FileNotFoundError("effective params missing")

    payload["review_pending"] = False
    offline_params = payload.get("offline_params", {})
    if isinstance(offline_params, dict):
        offline_params["review_pending"] = False
        payload["offline_params"] = offline_params
    current = payload.get("current", {})
    if isinstance(current, dict):
        current["review_completed_at"] = _now_iso()
        current["review_completed_by"] = source
        payload["current"] = current
    payload["updated_at"] = _now_iso()
    write_json(payload, EFFECTIVE_FILE)
    _append_history("complete_effective_review", {"source": source, "effective_file": str(EFFECTIVE_FILE)})
    return {"effective_file": str(EFFECTIVE_FILE)}


def rollback_effective_params(*, source: str = "manual", reason: str = "") -> Dict[str, Any]:
    """Rollback effective params to previous snapshot."""
    rollback = _read_json(ROLLBACK_FILE)
    effective_payload = rollback.get("effective_payload", {})
    if not isinstance(effective_payload, dict) or not effective_payload:
        raise FileNotFoundError("no rollback snapshot available")

    archived_current = {}
    if EFFECTIVE_FILE.exists():
        archived_current = _read_json(EFFECTIVE_FILE)

    restored = config_manager.normalize_effective_runtime_payload(effective_payload)
    restored["rolled_back_at"] = _now_iso()
    restored["rollout_state"] = "rolled_back"
    restored["rollback_reason"] = reason or "manual_rollback"
    restored["rollback_source"] = source
    write_json(restored, EFFECTIVE_FILE)
    _append_history(
        "rollback_effective_params",
        {
            "source": source,
            "reason": reason,
            "effective_file": str(EFFECTIVE_FILE),
            "previous_current": archived_current,
        },
    )
    return {
        "rolled_back": True,
        "effective_file": str(EFFECTIVE_FILE),
        "rollback_file": str(ROLLBACK_FILE),
    }


def build_shadow_payload(*args: Any, **kwargs: Any) -> Dict[str, Any]:
    """Build a shadow payload for parameter evaluation."""
    payload = args[0] if args else {}
    payload = payload if isinstance(payload, dict) else {}
    params = payload.get("suggested_params", {})
    if not isinstance(params, dict):
        params = {}
    return {
        "mode": "shadow",
        "generated_at": _now_iso(),
        "source": str(payload.get("source", kwargs.get("source", "param_flow")) or "param_flow"),
        "params": params,
        "suggestions": _suggestions_from_param_map(params, source="shadow", applied=False, auto_apply=False),
    }


def write_json(payload: Any, path: Path) -> None:
    """Write a JSON payload to disk atomically."""
    p = Path(path)
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")


def promote_suggestion_to_effective(
    suggestion_file: Optional[Path] = None,
    *,
    source: str = "manual_approve",
) -> Dict[str, Any]:
    """Promote a params suggestion to the effective params payload."""
    src = _resolve_suggestion_file(suggestion_file)
    if not src.exists():
        raise FileNotFoundError("suggestion file missing")

    suggestion_payload = _read_json(src)
    if not suggestion_payload:
        raise FileNotFoundError("suggestion payload empty")

    params = extract_param_map(suggestion_payload)
    if not params:
        raise ValueError("suggestion payload has no effective params")

    previous_effective = config_manager.load_effective_runtime_config()
    rollback_payload = {
        "created_at": _now_iso(),
        "source": source,
        "reason": "approve_to_shadow",
        "effective_payload": previous_effective,
    }
    write_json(rollback_payload, ROLLBACK_FILE)

    suggestions = suggestion_payload.get("suggestions", [])
    if not isinstance(suggestions, list) or not suggestions:
        suggestions = _suggestions_from_param_map(params, source=source, applied=True, auto_apply=False)

    normalized_suggestions = []
    for item in suggestions:
        if isinstance(item, dict):
            normalized = dict(item)
            normalized["applied"] = True
            normalized["auto_apply"] = False
            if normalized.get("applied_value") is None:
                normalized["applied_value"] = normalized.get("suggested_value")
            normalized_suggestions.append(normalized)

    effective_payload = {
        "params": dict(params),
        "suggestions": normalized_suggestions,
        "offline_params": {
            **dict(params),
            "review_pending": True,
        },
        "review_pending": True,
        "source": source,
        "approved_count": len(normalized_suggestions),
        "promoted_at": _now_iso(),
        "quality_context": suggestion_payload.get("quality_context", {}),
        "current": {
            "params": dict(params),
            "stats": suggestion_payload.get("stats", suggestion_payload.get("quality_context", {})),
            "method": "approved_suggestion",
            "applied_at": _now_iso(),
            "auto_apply": False,
            "applied": True,
            "source": source,
        },
        "history": [
            {
                "params": dict(params),
                "method": "approved_suggestion",
                "applied_at": _now_iso(),
                "source": source,
            }
        ],
        "updated_at": _now_iso(),
    }
    write_json(effective_payload, EFFECTIVE_FILE)

    shadow_payload = build_shadow_payload({"suggested_params": params, "source": source})
    write_json(shadow_payload, SHADOW_FILE)

    ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
    archived = ARCHIVE_DIR / f"params_applied_{int(time.time())}.json"
    write_json(suggestion_payload, archived)
    try:
        src.unlink()
    except Exception:
        pass

    _append_history(
        "promote_suggestion_to_effective",
        {
            "source": source,
            "suggestion_file": str(src),
            "effective_file": str(EFFECTIVE_FILE),
            "shadow_file": str(SHADOW_FILE),
            "archive_file": str(archived),
        },
    )
    return {
        "promoted": True,
        "effective_file": str(EFFECTIVE_FILE),
        "shadow_file": str(SHADOW_FILE),
        "archive_file": str(archived),
        "rollback_file": str(ROLLBACK_FILE),
    }


def reject_current_suggestion(*, source: str = "manual_reject") -> Dict[str, Any]:
    """Reject and archive the current params suggestion."""
    suggestion_path = _resolve_suggestion_file()
    if not suggestion_path.exists():
        raise FileNotFoundError("suggestion file missing")
    payload = _read_json(suggestion_path)
    ARCHIVE_DIR.mkdir(parents=True, exist_ok=True)
    archived = ARCHIVE_DIR / f"params_rejected_{int(time.time())}.json"
    archive_payload = {
        "rejected_at": _now_iso(),
        "rejected_by": source,
        "payload": payload,
    }
    write_json(archive_payload, archived)
    try:
        suggestion_path.unlink()
    except Exception:
        pass
    _append_history(
        "reject_current_suggestion",
        {"source": source, "archive_file": str(archived), "suggestion_file": str(suggestion_path)},
    )
    return {"rejected": True, "archive_file": str(archived)}
