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

from .tools import ToolError


class PlanContext:
    """Holds current plan JSON and provides validation/summary helpers."""

    def __init__(self, data: Optional[Dict[str, Any]] = None):
        self.data: Optional[Dict[str, Any]] = data

    @classmethod
    def from_file(cls, path: Path) -> "PlanContext":
        if not path.exists():
            raise ToolError(f"plan file does not exist: {path}")
        try:
            raw = path.read_text(encoding="utf-8")
        except Exception as err:
            raise ToolError(f"failed to read plan file: {err}") from err
        ctx = cls()
        ctx.update_from_json_text(raw)
        return ctx

    def update_from_json_text(self, text: str) -> None:
        try:
            data = json.loads(text)
        except json.JSONDecodeError as err:
            raise ToolError(f"Invalid plan JSON: {err}") from err
        self._validate_plan(data)
        self.data = data

    def _validate_plan(self, data: Dict[str, Any]) -> None:
        if not isinstance(data, dict) or "plan" not in data:
            raise ToolError("Plan JSON must be an object with a 'plan' field")
        plan_items = data.get("plan")
        if not isinstance(plan_items, list):
            raise ToolError("Plan JSON 'plan' must be a list")
        for idx, item in enumerate(plan_items, start=1):
            if not isinstance(item, dict):
                raise ToolError(f"Plan item {idx} must be an object")
            step = item.get("step")
            status = item.get("status")
            if not isinstance(step, str) or not step:
                raise ToolError(f"Plan item {idx} missing 'step'")
            if status not in {"pending", "in_progress", "completed"}:
                raise ToolError(f"Plan item {idx} has invalid status '{status}'")

    def summary(self, max_items: int = 5) -> str:
        if not self.data or "plan" not in self.data:
            return ""
        lines = []
        items = self.data.get("plan", [])
        for item in items[:max_items]:
            step = item.get("step", "")
            status = item.get("status", "")
            lines.append(f"- [{status}] {step}")
        if len(items) > max_items:
            lines.append(f"... {len(items) - max_items} more steps")
        return "\n".join(lines)

    def snapshot(self) -> Optional[Dict[str, Any]]:
        return self.data
