"""Tests for A2A Agent Card schema and skill definitions.

Validates that the Agent Card generated by Darwin matches the A2A v0.3.0+
specification and that all required fields are present and correctly typed.
"""

from __future__ import annotations

import json

import pytest

from darwin.agenticcloud.a2a import AgentCapabilities, AgentCard, AgentSkill
from darwin.agenticcloud.a2a.skills import (
    SKILL_IDENTITY,
    SKILL_RUN,
    SKILL_VERIFY,
    SKILLS,
    get_agent_card,
)

# -------------------------------------------------------------------
# Agent Card Schema Validation
# -------------------------------------------------------------------


class TestAgentCardSchema:
    """Validate Agent Card matches A2A v0.3.0+ schema."""

    def test_card_has_required_top_level_fields(self) -> None:
        """Agent Card must have name, description, url, version, skills."""
        card = get_agent_card()
        assert card.name
        assert card.description
        assert card.url
        assert card.version
        assert len(card.skills) >= 3

    def test_card_version_matches_package(self) -> None:
        """Version in the card must match darwin.agenticcloud.__version__."""
        from darwin import agenticcloud as dac

        card = get_agent_card()
        assert card.version == dac.__version__

    def test_card_has_provider(self) -> None:
        """Provider field must be present with organization."""
        card = get_agent_card()
        assert card.provider is not None
        assert card.provider.organization == "Darwin"
        assert card.provider.url is not None

    def test_card_has_capabilities(self) -> None:
        """Capabilities must be a valid AgentCapabilities object."""
        card = get_agent_card()
        assert isinstance(card.capabilities, AgentCapabilities)

    def test_card_default_input_output_modes(self) -> None:
        """Default I/O modes must include application/json."""
        card = get_agent_card()
        assert "application/json" in card.defaultInputModes
        assert "application/json" in card.defaultOutputModes

    def test_card_serializes_to_valid_json(self) -> None:
        """Card must serialize to valid JSON and deserialize back."""
        card = get_agent_card()
        json_str = card.model_dump_json()
        parsed = json.loads(json_str)

        assert parsed["name"] == "Darwin Agentic Cloud"
        assert "skills" in parsed
        assert len(parsed["skills"]) == 3

    def test_card_round_trip(self) -> None:
        """Card must survive a serialize → parse → reconstruct cycle."""
        card = get_agent_card()
        json_str = card.model_dump_json()
        restored = AgentCard.model_validate_json(json_str)
        assert restored == card

    def test_card_url_override(self) -> None:
        """Should accept a custom URL."""
        card = get_agent_card(url="https://custom.example.com")
        assert card.url == "https://custom.example.com"

    def test_card_version_override(self) -> None:
        """Should accept a custom version."""
        card = get_agent_card(version="99.0.0")
        assert card.version == "99.0.0"


# -------------------------------------------------------------------
# Skills Validation
# -------------------------------------------------------------------


class TestSkillDefinitions:
    """Validate skill definitions match A2A spec requirements."""

    @pytest.mark.parametrize("skill", SKILLS, ids=[s.id for s in SKILLS])
    def test_skill_has_required_fields(self, skill: AgentSkill) -> None:
        """Each skill must have id, name, description."""
        assert skill.id
        assert skill.name
        assert skill.description

    @pytest.mark.parametrize("skill", SKILLS, ids=[s.id for s in SKILLS])
    def test_skill_has_input_output_modes(self, skill: AgentSkill) -> None:
        """Each skill must declare supported input and output modes."""
        assert len(skill.inputModes) > 0
        assert len(skill.outputModes) > 0
        assert "application/json" in skill.inputModes
        assert "application/json" in skill.outputModes

    @pytest.mark.parametrize("skill", SKILLS, ids=[s.id for s in SKILLS])
    def test_skill_has_examples(self, skill: AgentSkill) -> None:
        """Each skill must have at least one example."""
        assert len(skill.examples) > 0
        for example in skill.examples:
            assert "input" in example
            assert "output" in example

    @pytest.mark.parametrize("skill", SKILLS, ids=[s.id for s in SKILLS])
    def test_skill_has_tags(self, skill: AgentSkill) -> None:
        """Each skill should have tags for discoverability."""
        assert len(skill.tags) > 0

    def test_three_core_skills_present(self) -> None:
        """Must have exactly darwin.run, darwin.verify, darwin.identity."""
        skill_ids = {s.id for s in SKILLS}
        assert skill_ids == {"darwin.run", "darwin.verify", "darwin.identity"}

    def test_run_skill_definition(self) -> None:
        """darwin.run skill has correct metadata."""
        assert SKILL_RUN.id == "darwin.run"
        assert (
            "execute" in SKILL_RUN.description.lower()
            or "workload" in SKILL_RUN.description.lower()
        )

    def test_verify_skill_definition(self) -> None:
        """darwin.verify skill has correct metadata."""
        assert SKILL_VERIFY.id == "darwin.verify"
        assert "verify" in SKILL_VERIFY.description.lower()

    def test_identity_skill_definition(self) -> None:
        """darwin.identity skill has correct metadata."""
        assert SKILL_IDENTITY.id == "darwin.identity"
        assert "identity" in SKILL_IDENTITY.description.lower()


# -------------------------------------------------------------------
# JSON Schema Compliance (hand-built from A2A spec)
# -------------------------------------------------------------------


class TestAgentCardJsonSchema:
    """Validate the serialized card against A2A v0.3.0+ JSON structure."""

    def _card_dict(self) -> dict:
        return get_agent_card().model_dump()

    def test_top_level_types(self) -> None:
        """All top-level fields have correct types."""
        d = self._card_dict()
        assert isinstance(d["name"], str)
        assert isinstance(d["description"], str)
        assert isinstance(d["url"], str)
        assert isinstance(d["version"], str)
        assert isinstance(d["capabilities"], dict)
        assert isinstance(d["defaultInputModes"], list)
        assert isinstance(d["defaultOutputModes"], list)
        assert isinstance(d["skills"], list)

    def test_capabilities_fields(self) -> None:
        """Capabilities must have boolean flags."""
        caps = self._card_dict()["capabilities"]
        assert isinstance(caps.get("streaming"), bool)
        assert isinstance(caps.get("pushNotifications"), bool)
        assert isinstance(caps.get("stateTransitionHistory"), bool)

    def test_skills_structure(self) -> None:
        """Each skill in the serialized card has correct structure."""
        skills = self._card_dict()["skills"]
        for skill in skills:
            assert isinstance(skill["id"], str)
            assert isinstance(skill["name"], str)
            assert isinstance(skill["description"], str)
            assert isinstance(skill["tags"], list)
            assert isinstance(skill["examples"], list)
            assert isinstance(skill["inputModes"], list)
            assert isinstance(skill["outputModes"], list)

    def test_provider_structure(self) -> None:
        """Provider must have organization string."""
        provider = self._card_dict()["provider"]
        assert isinstance(provider["organization"], str)

    def test_no_extra_unknown_fields(self) -> None:
        """Card should not have fields outside the A2A spec."""
        d = self._card_dict()
        allowed_top_level = {
            "name",
            "description",
            "url",
            "provider",
            "version",
            "documentationUrl",
            "capabilities",
            "authentication",
            "defaultInputModes",
            "defaultOutputModes",
            "skills",
        }
        assert set(d.keys()).issubset(allowed_top_level)
