"""File-backed workspace agent registry."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from .models import RegisteredAgent


class AgentRegistry:
    def __init__(self, workspace: str | Path) -> None:
        self.workspace = Path(workspace)
        self.path = self.workspace / "agents" / "registry.json"
        self.path.parent.mkdir(parents=True, exist_ok=True)
        if not self.path.exists():
            self._write_agents(_builtin_agents())

    def list_agents(self, *, include_disabled: bool = True) -> list[RegisteredAgent]:
        agents = self._read_agents()
        if include_disabled:
            return agents
        return [agent for agent in agents if agent.status == "active"]

    def list_active_agents(self) -> list[RegisteredAgent]:
        return self.list_agents(include_disabled=False)

    def get_agent(self, agent_id: str) -> RegisteredAgent | None:
        needle = agent_id.strip()
        for agent in self.list_agents():
            if agent.agent_id == needle:
                return agent
        return None

    def upsert_agent(self, payload: dict[str, Any] | RegisteredAgent) -> RegisteredAgent:
        agent = payload if isinstance(payload, RegisteredAgent) else RegisteredAgent.from_dict(payload)
        agents = self.list_agents()
        for index, existing in enumerate(agents):
            if existing.agent_id == agent.agent_id:
                if existing.source == "builtin" and agent.source == "workspace":
                    agent.source = "builtin"
                agent.created_at = existing.created_at
                agents[index] = agent
                self._write_agents(agents)
                return agent
        agents.append(agent)
        self._write_agents(agents)
        return agent

    def disable_agent(self, agent_id: str) -> RegisteredAgent:
        agents = self.list_agents()
        for index, agent in enumerate(agents):
            if agent.agent_id != agent_id:
                continue
            agent.status = "disabled"
            agents[index] = agent
            self._write_agents(agents)
            return agent
        raise ValueError(f"Unknown agent_id: {agent_id}")

    def delete_agent(self, agent_id: str) -> bool:
        target = agent_id.strip()
        if not target:
            return False
        agents = self.list_agents()
        kept = [agent for agent in agents if agent.agent_id != target]
        if len(kept) == len(agents):
            return False
        self._write_agents(kept)
        return True

    def search(
        self,
        *,
        role: str = "",
        capabilities: list[str] | None = None,
        tags: list[str] | None = None,
        skills: list[str] | None = None,
    ) -> list[RegisteredAgent]:
        role_terms = _terms(role)
        capability_terms = set(_terms(" ".join(capabilities or [])))
        tag_terms = set(_terms(" ".join(tags or [])))
        skill_terms = set(_terms(" ".join(skills or [])))
        matches: list[RegisteredAgent] = []
        for agent in self.list_active_agents():
            haystack = set(
                _terms(
                    " ".join(
                        [
                            agent.agent_id,
                            agent.name,
                            agent.display_name,
                            agent.role,
                            agent.description,
                            " ".join(agent.capabilities),
                            " ".join(agent.tags),
                            " ".join(agent.skill_names),
                        ]
                    )
                )
            )
            if role_terms and not role_terms.intersection(haystack):
                continue
            if capability_terms and not capability_terms.intersection(haystack):
                continue
            if tag_terms and not tag_terms.intersection(haystack):
                continue
            if skill_terms and not skill_terms.intersection(haystack):
                continue
            matches.append(agent)
        return matches

    def _read_agents(self) -> list[RegisteredAgent]:
        if not self.path.exists():
            return []
        payload = json.loads(self.path.read_text(encoding="utf-8"))
        raw_agents = payload.get("agents") if isinstance(payload, dict) else payload
        if not isinstance(raw_agents, list):
            return []
        return [RegisteredAgent.from_dict(item) for item in raw_agents if isinstance(item, dict)]

    def _write_agents(self, agents: list[RegisteredAgent]) -> None:
        self.path.parent.mkdir(parents=True, exist_ok=True)
        payload = {"version": 1, "agents": [agent.to_dict() for agent in agents]}
        self.path.write_text(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")


def _terms(text: str) -> set[str]:
    normalized = "".join(ch.lower() if ch.isalnum() else " " for ch in text)
    return {part for part in normalized.split() if part}


def _builtin_agents() -> list[RegisteredAgent]:
    return [
        RegisteredAgent(
            agent_id="researcher",
            name="researcher",
            display_name="Researcher",
            role="research",
            description="Finds facts, references, constraints, and implementation options.",
            system_prompt="You are a research specialist. Gather concise evidence and tradeoffs for the parent task.",
            capabilities=["research", "analysis", "source review", "requirements"],
            tags=["planning", "research"],
            priority=50,
            source="builtin",
        ),
        RegisteredAgent(
            agent_id="implementer",
            name="implementer",
            display_name="Implementer",
            role="implementation",
            description="Builds scoped implementation slices and proposes concrete changes.",
            system_prompt="You are an implementation specialist. Produce practical, scoped implementation output.",
            capabilities=["implementation", "coding", "refactor", "integration"],
            tags=["coding", "build"],
            priority=45,
            source="builtin",
        ),
        RegisteredAgent(
            agent_id="reviewer",
            name="reviewer",
            display_name="Reviewer",
            role="review",
            description="Reviews plans, code, outputs, and risks before final synthesis.",
            system_prompt="You are a review specialist. Focus on defects, missing requirements, and risks.",
            capabilities=["review", "quality", "risk", "verification"],
            tags=["review", "quality"],
            priority=45,
            source="builtin",
        ),
        RegisteredAgent(
            agent_id="tester",
            name="tester",
            display_name="Tester",
            role="testing",
            description="Designs and executes verification checks for task outputs.",
            system_prompt="You are a testing specialist. Identify focused checks and report pass/fail evidence.",
            capabilities=["testing", "verification", "regression", "qa"],
            tags=["test", "quality"],
            priority=40,
            source="builtin",
        ),
        RegisteredAgent(
            agent_id="documenter",
            name="documenter",
            display_name="Documenter",
            role="documentation",
            description="Writes and reconciles user-facing and internal documentation updates.",
            system_prompt="You are a documentation specialist. Produce concise docs aligned with the implementation.",
            capabilities=["documentation", "explanation", "migration notes", "release notes"],
            tags=["docs", "communication"],
            priority=35,
            source="builtin",
        ),
    ]
