from __future__ import annotations

import json
import os
from copy import deepcopy
from dataclasses import dataclass
from pathlib import Path
import tempfile
from typing import Any

from ..config import get_mcp_home
from .run_store import resolve_storage_path, utc_now


def get_build_assemblies_path() -> Path:
    custom_home = os.getenv("QINGFLOW_MCP_BUILD_HOME")
    if custom_home:
        return Path(custom_home).expanduser()
    return get_mcp_home() / "build-assemblies"


def default_manifest() -> dict[str, Any]:
    return {
        "solution_name": "",
        "summary": None,
        "business_context": {},
        "package": {"enabled": True, "ordinal": 1},
        "entities": [],
        "roles": [],
        "requirements": [],
        "success_metrics": [],
        "portal": {"enabled": False, "sections": []},
        "navigation": {"enabled": False, "items": []},
        "publish_policy": {"apps": True, "portal": True, "navigation": True},
        "preferences": {
            "multi_app": None,
            "create_package": True,
            "create_portal": False,
            "create_navigation": False,
            "naming_style": "title",
        },
        "assumptions": [],
        "constraints": [],
        "metadata": {},
    }


def default_artifacts() -> dict[str, Any]:
    return {
        "package": {},
        "roles": {},
        "apps": {},
        "views": {},
        "charts": {},
        "records": {},
        "portal": {},
        "navigation": {},
        "field_maps": {},
    }


def default_builder_state() -> dict[str, Any]:
    return {
        "generated_specs": {},
        "failure_signatures": {},
        "last_failure_signature": None,
        "last_failure_stage": None,
        "last_failure_count": 0,
    }


@dataclass(slots=True)
class BuildAssemblyStore:
    path: Path
    data: dict[str, Any]

    @classmethod
    def open(cls, *, build_id: str, create: bool = True) -> "BuildAssemblyStore":
        base_dir = get_build_assemblies_path()
        try:
            base_dir.mkdir(parents=True, exist_ok=True)
        except PermissionError:
            base_dir = Path(tempfile.gettempdir()) / "qingflow-mcp-build-assemblies"
            base_dir.mkdir(parents=True, exist_ok=True)
        path = resolve_storage_path(base_dir, key=build_id, id_field="build_id")
        if path.exists():
            data = json.loads(path.read_text(encoding="utf-8"))
            stored_build_id = data.get("build_id")
            if stored_build_id != build_id:
                raise ValueError(f"existing build assembly at '{path}' belongs to '{stored_build_id}', not '{build_id}'")
        elif not create:
            raise FileNotFoundError(build_id)
        else:
            data = {
                "build_id": build_id,
                "status": "draft",
                "manifest": default_manifest(),
                "stage_specs": {
                    "app_flow": {},
                    "views": {},
                    "analytics_portal": {},
                    "navigation": {},
                },
                "artifacts": default_artifacts(),
                "builder_state": default_builder_state(),
                "stage_history": [],
                "created_at": utc_now(),
                "updated_at": utc_now(),
            }
            try:
                path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
            except PermissionError:
                base_dir = Path(tempfile.gettempdir()) / "qingflow-mcp-build-assemblies"
                base_dir.mkdir(parents=True, exist_ok=True)
                path = resolve_storage_path(base_dir, key=build_id, id_field="build_id")
                path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
        return cls(path=path, data=data)

    def get_manifest(self) -> dict[str, Any]:
        return deepcopy(self.data.get("manifest", default_manifest()))

    def set_manifest(self, manifest: dict[str, Any]) -> None:
        self.data["manifest"] = deepcopy(manifest)
        self._flush()

    def get_artifacts(self) -> dict[str, Any]:
        return deepcopy(self.data.get("artifacts", default_artifacts()))

    def set_artifacts(self, artifacts: dict[str, Any]) -> None:
        self.data["artifacts"] = deepcopy(artifacts)
        self._flush()

    def set_stage_spec(self, stage_name: str, spec: dict[str, Any]) -> None:
        self.data.setdefault("stage_specs", {})
        self.data["stage_specs"][stage_name] = deepcopy(spec)
        self._flush()

    def add_stage_history(self, entry: dict[str, Any]) -> None:
        self.data.setdefault("stage_history", []).append(deepcopy(entry))
        self._flush()

    def mark_status(self, status: str) -> None:
        self.data["status"] = status
        self._flush()

    def get_builder_state(self) -> dict[str, Any]:
        return deepcopy(self.data.get("builder_state", default_builder_state()))

    def set_builder_state(self, builder_state: dict[str, Any]) -> None:
        self.data["builder_state"] = deepcopy(builder_state)
        self._flush()

    def get_builder_value(self, key: str, default: Any = None) -> Any:
        return deepcopy(self.data.get("builder_state", {}).get(key, default))

    def set_builder_value(self, key: str, value: Any) -> None:
        self.data.setdefault("builder_state", default_builder_state())
        self.data["builder_state"][key] = deepcopy(value)
        self._flush()

    def summary(self) -> dict[str, Any]:
        return {
            "build_id": self.data["build_id"],
            "status": self.data["status"],
            "manifest": deepcopy(self.data.get("manifest", {})),
            "artifacts": deepcopy(self.data.get("artifacts", {})),
            "builder_state": deepcopy(self.data.get("builder_state", {})),
            "stage_specs": deepcopy(self.data.get("stage_specs", {})),
            "stage_history": deepcopy(self.data.get("stage_history", [])),
            "build_path": str(self.path),
        }

    def _flush(self) -> None:
        self.data["updated_at"] = utc_now()
        try:
            self.path.write_text(json.dumps(self.data, ensure_ascii=False, indent=2), encoding="utf-8")
        except PermissionError:
            fallback_dir = Path(tempfile.gettempdir()) / "qingflow-mcp-build-assemblies"
            fallback_dir.mkdir(parents=True, exist_ok=True)
            self.path = resolve_storage_path(fallback_dir, key=str(self.data.get("build_id") or "build"), id_field="build_id")
            self.path.write_text(json.dumps(self.data, ensure_ascii=False, indent=2), encoding="utf-8")
