"""Tests for the spawn_sub_agent skill."""

from __future__ import annotations

import json
import sys
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path
from typing import Any

import pytest

from core.skills import Skill, SkillLoader


def _load_handler_module(module_name: str = "spawn_sub_agent_handler"):
    """Import spawn_sub_agent handler module from file path."""
    handler_path = Path(__file__).parent.parent / ".chatixia" / "skills" / "spawn_sub_agent" / "handler.py"
    spec = spec_from_file_location(module_name, handler_path)
    assert spec is not None and spec.loader is not None
    module = module_from_spec(spec)
    sys.modules[module_name] = module
    spec.loader.exec_module(module)
    return module


class TestSpawnSubAgentSkillLoading:
    def test_skill_loads_with_handler(self) -> None:
        loader = SkillLoader(Path("skills"))
        skills = loader.load_all()

        assert "spawn_sub_agent" in skills
        assert skills["spawn_sub_agent"].has_handler

    def test_skill_exposes_expected_parameters(self) -> None:
        loader = SkillLoader(Path("skills"))
        skills = loader.load_all()
        skill = skills["spawn_sub_agent"]

        param_names = {p.name for p in skill.parameters}
        assert {
            "mode",
            "agent_id",
            "name",
            "goal",
            "model",
            "prompt",
            "tools",
            "user_message",
            "max_turns",
            "temperature",
            "registry_path",
        } <= param_names


class TestSpawnSubAgentHandler:
    def test_create_list_get_delete_cycle(self, tmp_path: Path) -> None:
        module = _load_handler_module("spawn_sub_agent_handler_cycle")
        registry = tmp_path / "agents.json"

        created = json.loads(
            module.handle(
                mode="create_agent",
                name="Contract Analyst",
                goal="Review contract clauses",
                model="gpt-4.1-mini",
                prompt="Use only read tools and summarize risks",
                tools='["echo"]',
                registry_path=str(registry),
            )
        )
        assert created["ok"] is True
        agent_id = created["agent"]["agent_id"]

        listed = json.loads(module.handle(mode="list_agents", registry_path=str(registry)))
        assert listed["ok"] is True
        assert listed["count"] == 1
        assert listed["agents"][0]["agent_id"] == agent_id

        fetched = json.loads(
            module.handle(
                mode="get_agent",
                agent_id=agent_id,
                registry_path=str(registry),
            )
        )
        assert fetched["ok"] is True
        assert fetched["agent"]["goal"] == "Review contract clauses"

        deleted = json.loads(
            module.handle(
                mode="delete_agent",
                agent_id=agent_id,
                registry_path=str(registry),
            )
        )
        assert deleted["ok"] is True
        assert deleted["deleted"] is True

    def test_create_rejects_missing_fundamental(self, tmp_path: Path) -> None:
        module = _load_handler_module("spawn_sub_agent_handler_missing_model")
        result = json.loads(
            module.handle(
                mode="create_agent",
                name="A",
                goal="B",
                prompt="C",
                tools='["echo"]',
                registry_path=str(tmp_path / "agents.json"),
            )
        )
        assert result["ok"] is False
        assert "model is required" in result["error"]

    def test_create_rejects_recursive_tool(self, tmp_path: Path) -> None:
        module = _load_handler_module("spawn_sub_agent_handler_recursive")
        result = json.loads(
            module.handle(
                mode="create_agent",
                name="Recursive Agent",
                goal="Spawn agents",
                model="gpt-4.1-mini",
                prompt="Try recursion",
                tools='["spawn_sub_agent"]',
                registry_path=str(tmp_path / "agents.json"),
            )
        )
        assert result["ok"] is False
        assert "forbidden tools" in result["error"]

    def test_run_agent_executes_with_azure_backend(self, tmp_path: Path, monkeypatch) -> None:
        module = _load_handler_module("spawn_sub_agent_handler_run")
        registry = tmp_path / "agents.json"

        created = json.loads(
            module.handle(
                mode="create_agent",
                name="Echo Specialist",
                goal="Echo exactly",
                model="subagent-deployment",
                prompt="Use echo when needed",
                tools='["echo"]',
                registry_path=str(registry),
            )
        )
        agent_id = created["agent"]["agent_id"]

        fake_client = object()
        monkeypatch.setattr(module, "_create_azure_client", lambda: fake_client)

        echo_skill = Skill(
            name="echo",
            description="Echo",
            version="1.0.0",
            prompt_template="",
            handler=lambda message: message,
        )
        monkeypatch.setattr(module, "_load_available_skills", lambda: {"echo": echo_skill})

        captured: dict[str, Any] = {}

        class FakeExecutor:
            def __init__(self, client, deployment, system_prompt, agent_logger=None):
                captured["client"] = client
                captured["deployment"] = deployment
                captured["system_prompt"] = system_prompt
                captured["agent_logger"] = agent_logger

            def run(
                self,
                user_message,
                skills,
                messages,
                temperature,
                max_turns,
                session_id,
                context_injection=None,
                user_content=None,
                attachment_meta=None,
            ):
                captured["user_message"] = user_message
                captured["skills"] = skills
                captured["temperature"] = temperature
                captured["max_turns"] = max_turns
                captured["session_id"] = session_id

                messages.append(
                    {
                        "role": "tool",
                        "name": "echo",
                        "tool_call_id": "call_1",
                        "content": "hello from tool",
                        "_metadata": {"duration_ms": 12},
                    }
                )
                return "final answer"

        monkeypatch.setattr(module, "SkillExecutor", FakeExecutor)

        ran = json.loads(
            module.handle(
                mode="run_agent",
                agent_id=agent_id,
                user_message="Say hello",
                temperature=0.4,
                max_turns=5,
                registry_path=str(registry),
            )
        )

        assert ran["ok"] is True
        assert ran["final_response"] == "final answer"
        assert ran["model_used"] == "subagent-deployment"
        assert len(ran["tool_events"]) == 1
        assert ran["tool_events"][0]["tool"] == "echo"
        assert ran["tool_events"][0]["duration_ms"] == 12

        assert captured["client"] is fake_client
        assert captured["deployment"] == "subagent-deployment"
        assert captured["user_message"] == "Say hello"
        assert set(captured["skills"].keys()) == {"echo"}
        assert captured["temperature"] == 0.4
        assert captured["max_turns"] == 5

    def test_run_agent_rejects_missing_tool_config(self, tmp_path: Path, monkeypatch) -> None:
        module = _load_handler_module("spawn_sub_agent_handler_missing_tool")
        registry = tmp_path / "agents.json"

        created = json.loads(
            module.handle(
                mode="create_agent",
                name="Missing Tool Agent",
                goal="Use unknown tool",
                model="subagent-deployment",
                prompt="Do work",
                tools='["unknown_tool"]',
                registry_path=str(registry),
            )
        )
        agent_id = created["agent"]["agent_id"]

        monkeypatch.setattr(module, "_load_available_skills", lambda: {})
        monkeypatch.setattr(module, "_create_azure_client", lambda: object())

        ran = json.loads(
            module.handle(
                mode="run_agent",
                agent_id=agent_id,
                user_message="Do the task",
                registry_path=str(registry),
            )
        )

        assert ran["ok"] is False
        assert ran["error"] == "Sub-agent has invalid tool configuration"
        assert ran["missing_tools"] == ["unknown_tool"]


@pytest.mark.parametrize("bad_mode", ["", "invalid", "run"])
def test_handler_invalid_mode(bad_mode: str, tmp_path: Path) -> None:
    module = _load_handler_module(f"spawn_sub_agent_invalid_mode_{bad_mode or 'empty'}")
    result = json.loads(module.handle(mode=bad_mode, registry_path=str(tmp_path / "agents.json")))
    assert result["ok"] is False
    assert "Invalid mode" in result["error"]
