"""
Local file-based agent builder registry for testing.

Stores agents in a local folder structure:
agents/
  {agent_id}/
    metadata.json  # name, description, tools, html, details, schedule
    code.py        # Python code
"""

from __future__ import annotations

import json
import uuid
from pathlib import Path
from typing import Any

from universal_mcp.agentr.types import AgentResponse, TemplateResponse


class LocalAgentBuilderRegistry:
    """
    File-based agent builder registry for local development and testing.

    Simplified version that only supports create and update operations.
    Mock support for templates and scheduling.
    """

    def __init__(
        self,
        *,
        base_path: str | Path = ".agents",
        default_agent_id: str | None = None,
        default_template_id: str | None = None,
        user_id: str = "test_user",
        org_id: str = "test_org",
    ):
        self.base_path = Path(base_path).resolve()
        self.default_agent_id = default_agent_id
        self.default_template_id = default_template_id
        self.user_id = user_id
        self.org_id = org_id

        # Create base directory if it doesn't exist
        self.base_path.mkdir(parents=True, exist_ok=True)

    def _get_agent_path(self, agent_id: str) -> Path:
        """Get the directory path for an agent."""
        return self.base_path / agent_id

    def _get_metadata_path(self, agent_id: str) -> Path:
        """Get the metadata.json path for an agent."""
        return self._get_agent_path(agent_id) / "metadata.json"

    def _get_code_path(self, agent_id: str) -> Path:
        """Get the code.py path for an agent."""
        return self._get_agent_path(agent_id) / "code.py"

    async def get_agent(self, agent_id: str | None = None) -> AgentResponse | TemplateResponse | None:
        """Get an agent by ID or fallback to template."""
        use_agent_id = agent_id or self.default_agent_id

        # Try to find agent first
        if use_agent_id:
            agent_path = self._get_agent_path(use_agent_id)
            if agent_path.exists():
                metadata_path = self._get_metadata_path(use_agent_id)
                code_path = self._get_code_path(use_agent_id)

                if metadata_path.exists():
                    with open(metadata_path) as f:
                        metadata = json.load(f)

                    # Read code if exists
                    code = None
                    if code_path.exists():
                        with open(code_path) as f:
                            code = f.read()

                    # Build instructions
                    instructions = metadata.get("instructions", {})
                    if code:
                        instructions["script"] = code

                    return AgentResponse(
                        id=use_agent_id,
                        name=metadata.get("name"),
                        description=metadata.get("description"),
                        user_id=self.user_id,
                        org_id=self.org_id,
                        instructions=instructions,
                        tools=metadata.get("tools"),
                        created_at=metadata.get("created_at", ""),
                        updated_at=metadata.get("updated_at", ""),
                        html=metadata.get("html"),
                        details=metadata.get("details"),
                    )

        # Fallback to template if configured
        if self.default_template_id:
            # For now, just return a mock template response if ID matches or if we just want the default
            # In a real local impl, we might store templates in a 'templates' folder
            return TemplateResponse(
                id=self.default_template_id,
                name="Mock Template",
                description="A mock template for testing",
                user_id=self.user_id,
                org_id=self.org_id,
                instructions={},
                tools={},
                created_at="2026-01-01T00:00:00Z",
                updated_at="2026-01-01T00:00:00Z",
            )

        return None

    async def create_agent(
        self,
        *,
        name: str | None = None,
        description: str | None = None,
        instructions: dict[str, Any] | None = None,
        tools: dict[str, list[str]] | None = None,
        html: str | None = None,
        details: str | None = None,
    ) -> AgentResponse:
        """Create a new agent."""
        agent_id = str(uuid.uuid4())
        agent_path = self._get_agent_path(agent_id)
        agent_path.mkdir(parents=True, exist_ok=True)

        # Extract script from instructions
        script = None
        instructions_without_script = {}
        if instructions:
            script = instructions.get("script")
            instructions_without_script = {k: v for k, v in instructions.items() if k != "script"}

        # Write metadata
        metadata = {
            "name": name,
            "description": description,
            "tools": tools,
            "instructions": instructions_without_script,
            "html": html,
            "details": details,
            "created_at": "2026-01-17T00:00:00Z",
            "updated_at": "2026-01-17T00:00:00Z",
        }

        with open(self._get_metadata_path(agent_id), "w") as f:
            json.dump(metadata, f, indent=2)

        # Write code if provided
        if script:
            with open(self._get_code_path(agent_id), "w") as f:
                f.write(script)

        return AgentResponse(
            id=agent_id,
            name=name,
            description=description,
            user_id=self.user_id,
            org_id=self.org_id,
            instructions=instructions,
            tools=tools,
            created_at=metadata["created_at"],
            updated_at=metadata["updated_at"],
            html=html,
            details=details,
        )

    async def update_agent(
        self,
        *,
        agent_id: str | None = None,
        name: str | None = None,
        description: str | None = None,
        instructions: dict[str, Any] | None = None,
        tools: dict[str, list[str]] | None = None,
        html: str | None = None,
        details: str | None = None,
    ) -> AgentResponse:
        """Update an existing agent."""
        use_agent_id = agent_id or self.default_agent_id
        if not use_agent_id:
            raise ValueError("agent_id not provided and no default_agent_id set")

        agent_path = self._get_agent_path(use_agent_id)
        if not agent_path.exists():
            raise ValueError(f"Agent {use_agent_id} does not exist")

        # Read existing metadata
        with open(self._get_metadata_path(use_agent_id)) as f:
            metadata = json.load(f)

        # Update fields
        if name is not None:
            metadata["name"] = name
        if description is not None:
            metadata["description"] = description
        if tools is not None:
            metadata["tools"] = tools
        if html is not None:
            metadata["html"] = html
        if details is not None:
            metadata["details"] = details

        # Handle instructions
        if instructions is not None:
            script = instructions.get("script")
            instructions_without_script = {k: v for k, v in instructions.items() if k != "script"}
            metadata["instructions"] = instructions_without_script

            # Update code file
            if script:
                with open(self._get_code_path(use_agent_id), "w") as f:
                    f.write(script)

        metadata["updated_at"] = "2026-01-17T00:00:00Z"

        # Write updated metadata
        with open(self._get_metadata_path(use_agent_id), "w") as f:
            json.dump(metadata, f, indent=2)

        return await self.get_agent(use_agent_id)

    async def schedule_agent(self, cron: str, agent_id: str | None = None) -> None:
        """Mock scheduling by updating metadata."""
        use_agent_id = agent_id or self.default_agent_id
        if not use_agent_id:
            raise ValueError("agent_id not provided and no default_agent_id set")

        agent_path = self._get_agent_path(use_agent_id)
        if not agent_path.exists():
            raise ValueError(f"Agent {use_agent_id} does not exist")

        with open(self._get_metadata_path(use_agent_id)) as f:
            metadata = json.load(f)

        metadata["schedule"] = cron

        with open(self._get_metadata_path(use_agent_id), "w") as f:
            json.dump(metadata, f, indent=2)

    async def upsert_agent(
        self,
        *,
        name: str | None = None,
        description: str | None = None,
        instructions: dict[str, Any] | None = None,
        tools: dict[str, list[str]] | None = None,
        cron: str | None = None,
        html: str | None = None,
        details: str | None = None,
    ) -> tuple[AgentResponse, bool]:
        """
        Create or update an agent.

        Returns:
            Tuple of (agent_response, was_update)
        """
        if self.default_agent_id:
            res = await self.update_agent(
                agent_id=self.default_agent_id,
                name=name,
                description=description,
                instructions=instructions,
                tools=tools,
                html=html,
                details=details,
            )
            is_update = True
        else:
            res = await self.create_agent(
                name=name,
                description=description,
                instructions=instructions,
                tools=tools,
                html=html,
                details=details,
            )
            is_update = False

        if cron:
            await self.schedule_agent(cron=cron, agent_id=str(res.id))

        return res, is_update
