"""Unified runtime context — local state + safe server metadata sync (P2 §3)."""

from __future__ import annotations

import json
import threading
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

_LOCK = threading.Lock()
_CONTEXT_PATH = Path.home() / ".config" / "iuve-connect-agent" / "runtime-context.json"


@dataclass
class RuntimeContext:
    current_goal: str = ""
    current_plan: str = ""
    current_step: str = ""
    execution_status: str = ""
    active_window: str = ""
    active_browser: str = ""
    current_url: str = ""
    current_workspace: str = ""
    current_file: str = ""
    active_composer: str = ""
    current_artifacts: list[str] = field(default_factory=list)
    updated_at: str = ""

    def as_dict(self) -> dict[str, Any]:
        return asdict(self)

    def safe_metadata(self) -> dict[str, Any]:
        """Fields safe to sync to Orchestra without screen/document content."""
        return {
            "current_goal": self.current_goal[:500],
            "current_plan": self.current_plan[:128],
            "current_step": self.current_step[:128],
            "execution_status": self.execution_status[:32],
            "active_window": self.active_window[:256],
            "active_browser": self.active_browser[:128],
            "current_url": self.current_url[:2048],
            "current_workspace": self.current_workspace[:512],
            "current_file": self.current_file[:512],
            "active_composer": self.active_composer[:128],
            "current_artifacts": [str(a)[:256] for a in self.current_artifacts[:32]],
            "updated_at": self.updated_at,
        }

    @classmethod
    def from_dict(cls, raw: dict[str, Any] | None) -> RuntimeContext:
        if not isinstance(raw, dict):
            return cls()
        artifacts = [str(a) for a in (raw.get("current_artifacts") or []) if str(a).strip()]
        return cls(
            current_goal=str(raw.get("current_goal") or ""),
            current_plan=str(raw.get("current_plan") or ""),
            current_step=str(raw.get("current_step") or ""),
            execution_status=str(raw.get("execution_status") or ""),
            active_window=str(raw.get("active_window") or ""),
            active_browser=str(raw.get("active_browser") or ""),
            current_url=str(raw.get("current_url") or ""),
            current_workspace=str(raw.get("current_workspace") or ""),
            current_file=str(raw.get("current_file") or ""),
            active_composer=str(raw.get("active_composer") or ""),
            current_artifacts=artifacts,
            updated_at=str(raw.get("updated_at") or ""),
        )


class RuntimeContextStore:
    def __init__(self, path: Path | None = None) -> None:
        self._path = (path or _CONTEXT_PATH).expanduser()

    def load(self) -> RuntimeContext:
        with _LOCK:
            if not self._path.is_file():
                return RuntimeContext()
            try:
                raw = json.loads(self._path.read_text(encoding="utf-8"))
                if isinstance(raw, dict):
                    return RuntimeContext.from_dict(raw)
            except Exception:
                pass
            return RuntimeContext()

    def save(self, ctx: RuntimeContext) -> None:
        with _LOCK:
            self._path.parent.mkdir(parents=True, exist_ok=True)
            payload = ctx.as_dict()
            self._path.write_text(json.dumps(payload, ensure_ascii=True, indent=2), encoding="utf-8")

    def update(self, **fields: Any) -> RuntimeContext:
        ctx = self.load()
        for key, value in fields.items():
            if not hasattr(ctx, key):
                continue
            if key == "current_artifacts" and isinstance(value, list):
                setattr(ctx, key, [str(v) for v in value if str(v).strip()])
            else:
                setattr(ctx, key, str(value) if value is not None else "")
        ctx.updated_at = datetime.now(timezone.utc).isoformat()
        self.save(ctx)
        return ctx

    def sync_safe_metadata_best_effort(self) -> None:
        try:
            from .orchestra_runtime_sync import sync_runtime_context_to_server

            sync_runtime_context_to_server(self.load().safe_metadata())
        except Exception:
            pass


def runtime_context_store() -> RuntimeContextStore:
    return RuntimeContextStore()


def update_from_plan(*, plan_id: str, goal_summary: str, step_id: str, command_type: str) -> None:
    store = runtime_context_store()
    store.update(
        current_goal=goal_summary,
        current_plan=plan_id,
        current_step=f"{step_id}:{command_type}",
        execution_status="executing",
    )
    store.sync_safe_metadata_best_effort()


def finalize_plan_execution(*, status: str, plan_id: str = "", message: str = "") -> None:
    """Mark plan execution finished so web UI can stop showing 'agent working'."""
    normalized = str(status or "idle").strip().lower()
    if normalized not in {"completed", "failed", "cancelled", "idle", "error"}:
        normalized = "idle"
    store = runtime_context_store()
    fields: dict[str, Any] = {
        "execution_status": normalized,
        "current_step": "",
    }
    if plan_id:
        fields["current_plan"] = plan_id
    if message:
        fields["current_goal"] = message[:500]
    store.update(**fields)
    store.sync_safe_metadata_best_effort()


def update_from_goal_memory(snapshot: dict[str, Any]) -> None:
    if not isinstance(snapshot, dict):
        return
    store = runtime_context_store()
    fields: dict[str, Any] = {}
    if snapshot.get("current_goal"):
        fields["current_goal"] = snapshot["current_goal"]
    if snapshot.get("active_site"):
        fields["current_url"] = snapshot["active_site"]
    if snapshot.get("active_composer"):
        fields["active_composer"] = snapshot["active_composer"]
    if snapshot.get("last_written_file_path"):
        fields["current_file"] = snapshot["last_written_file_path"]
    if snapshot.get("last_browser_url"):
        fields["current_url"] = snapshot["last_browser_url"]
    if fields:
        store.update(**fields)
        store.sync_safe_metadata_best_effort()


def update_from_web_operator(*, url: str = "", browser: str = "", composer: str = "") -> None:
    fields: dict[str, Any] = {}
    if url:
        fields["current_url"] = url
    if browser:
        fields["active_browser"] = browser
    if composer:
        fields["active_composer"] = composer
    if fields:
        store = runtime_context_store()
        store.update(**fields)
        store.sync_safe_metadata_best_effort()
