"""Tests for schema validation engine."""

from __future__ import annotations

import pytest

from mcp_forge._types import ContentBlock, ToolInfo, ToolResult
from mcp_forge.schema import (
    diff_schemas,
    generate_sample_input,
    validate_schema_is_valid,
    validate_tool_input,
    validate_tool_output,
)


class TestValidateToolInput:
    """Test input validation against tool schemas."""

    def test_valid_input(self):
        tool = ToolInfo(
            name="test",
            input_schema={
                "type": "object",
                "required": ["name"],
                "properties": {"name": {"type": "string"}},
            },
        )
        errors = validate_tool_input(tool, {"name": "Alice"})
        assert errors == []

    def test_missing_required(self):
        tool = ToolInfo(
            name="test",
            input_schema={
                "type": "object",
                "required": ["name"],
                "properties": {"name": {"type": "string"}},
            },
        )
        errors = validate_tool_input(tool, {})
        assert len(errors) > 0
        assert any("name" in e for e in errors)

    def test_wrong_type(self):
        tool = ToolInfo(
            name="test",
            input_schema={
                "type": "object",
                "required": ["count"],
                "properties": {"count": {"type": "integer"}},
            },
        )
        errors = validate_tool_input(tool, {"count": "not_a_number"})
        assert len(errors) > 0

    def test_empty_schema(self):
        tool = ToolInfo(name="test", input_schema={})
        errors = validate_tool_input(tool, {"anything": "goes"})
        assert errors == []


class TestValidateToolOutput:
    """Test output validation against schemas."""

    def test_valid_json_output(self):
        result = ToolResult(
            content=[ContentBlock(type="text", text='{"status": "ok", "count": 5}')]
        )
        schema = {
            "type": "object",
            "required": ["status"],
            "properties": {
                "status": {"type": "string"},
                "count": {"type": "integer"},
            },
        }
        errors = validate_tool_output(result, schema)
        assert errors == []

    def test_invalid_json_output(self):
        result = ToolResult(
            content=[ContentBlock(type="text", text="not json")]
        )
        errors = validate_tool_output(result, {"type": "object"})
        assert len(errors) > 0
        assert any("not valid JSON" in e for e in errors)

    def test_schema_violation(self):
        result = ToolResult(
            content=[ContentBlock(type="text", text='{"status": 123}')]
        )
        schema = {
            "type": "object",
            "properties": {"status": {"type": "string"}},
        }
        errors = validate_tool_output(result, schema)
        assert len(errors) > 0

    def test_empty_output(self):
        result = ToolResult(content=[])
        errors = validate_tool_output(result, {"type": "object"})
        assert len(errors) > 0
        assert any("empty" in e.lower() for e in errors)


class TestValidateSchemaIsValid:
    """Test meta-validation of JSON Schemas."""

    def test_valid_schema(self):
        schema = {
            "type": "object",
            "required": ["name"],
            "properties": {"name": {"type": "string"}},
        }
        errors = validate_schema_is_valid(schema)
        assert errors == []

    def test_missing_type(self):
        schema = {"properties": {"name": {"type": "string"}}}
        errors = validate_schema_is_valid(schema)
        assert any("type" in e.lower() for e in errors)

    def test_required_not_in_properties(self):
        schema = {
            "type": "object",
            "required": ["missing_field"],
            "properties": {"name": {"type": "string"}},
        }
        errors = validate_schema_is_valid(schema)
        assert any("missing_field" in e for e in errors)

    def test_non_dict_schema(self):
        errors = validate_schema_is_valid("not a dict")  # type: ignore
        assert len(errors) > 0


class TestDiffSchemas:
    """Test schema diffing."""

    def test_identical_schemas(self):
        schema = {"type": "object", "properties": {"x": {"type": "string"}}}
        changes = diff_schemas(schema, schema)
        assert changes == []

    def test_added_property(self):
        old = {"type": "object", "properties": {"x": {"type": "string"}}}
        new = {
            "type": "object",
            "properties": {
                "x": {"type": "string"},
                "y": {"type": "integer"},
            },
        }
        changes = diff_schemas(old, new)
        assert len(changes) > 0
        assert any("Added" in c or "added" in c.lower() for c in changes)

    def test_removed_property(self):
        old = {
            "type": "object",
            "properties": {
                "x": {"type": "string"},
                "y": {"type": "integer"},
            },
        }
        new = {"type": "object", "properties": {"x": {"type": "string"}}}
        changes = diff_schemas(old, new)
        assert len(changes) > 0
        assert any("Removed" in c or "removed" in c.lower() for c in changes)

    def test_type_change(self):
        old = {"type": "object", "properties": {"x": {"type": "string"}}}
        new = {"type": "object", "properties": {"x": {"type": "integer"}}}
        changes = diff_schemas(old, new)
        assert len(changes) > 0


class TestGenerateSampleInput:
    """Test sample input generation from schemas."""

    def test_string_field(self):
        schema = {
            "type": "object",
            "required": ["name"],
            "properties": {"name": {"type": "string"}},
        }
        sample = generate_sample_input(schema)
        assert "name" in sample
        assert isinstance(sample["name"], str)

    def test_integer_field(self):
        schema = {
            "type": "object",
            "required": ["count"],
            "properties": {"count": {"type": "integer"}},
        }
        sample = generate_sample_input(schema)
        assert "count" in sample
        assert isinstance(sample["count"], int)

    def test_boolean_field(self):
        schema = {
            "type": "object",
            "required": ["enabled"],
            "properties": {"enabled": {"type": "boolean"}},
        }
        sample = generate_sample_input(schema)
        assert "enabled" in sample
        assert isinstance(sample["enabled"], bool)

    def test_enum_field(self):
        schema = {
            "type": "object",
            "required": ["color"],
            "properties": {"color": {"type": "string", "enum": ["red", "blue"]}},
        }
        sample = generate_sample_input(schema)
        assert sample["color"] == "red"

    def test_optional_fields_excluded(self):
        schema = {
            "type": "object",
            "required": ["name"],
            "properties": {
                "name": {"type": "string"},
                "optional_field": {"type": "string"},
            },
        }
        sample = generate_sample_input(schema)
        assert "name" in sample
        assert "optional_field" not in sample

    def test_non_object_schema(self):
        schema = {"type": "string"}
        sample = generate_sample_input(schema)
        assert sample == {}
