"""
Tests for Validators.

This module provides comprehensive tests for the validation utilities
in the torivers_sdk.validators module.
"""

from __future__ import annotations

import tempfile
from pathlib import Path
from typing import Any

import pytest

from torivers_sdk.validators import (
    ManifestValidator,
    SchemaValidationError,
    SchemaValidationResult,
    SchemaValidator,
    SecurityIssue,
    SecurityResult,
    SecurityValidator,
    ValidationIssue,
    ValidationLevel,
    ValidationResult,
)

# =============================================================================
# ManifestValidator Tests
# =============================================================================


class TestManifestValidatorConstants:
    """Test ManifestValidator constants and configuration."""

    def test_required_fields_defined(self) -> None:
        """Test that REQUIRED_FIELDS constant is defined."""
        assert hasattr(ManifestValidator, "REQUIRED_FIELDS")
        assert isinstance(ManifestValidator.REQUIRED_FIELDS, list)
        assert len(ManifestValidator.REQUIRED_FIELDS) > 0

    def test_required_fields_contains_expected(self) -> None:
        """Test REQUIRED_FIELDS contains expected fields."""
        required = ManifestValidator.REQUIRED_FIELDS
        assert "name" in required
        assert "version" in required
        assert "description" in required
        assert "kind" in required

    def test_allowed_categories_defined(self) -> None:
        """Test that ALLOWED_CATEGORIES constant is defined."""
        assert hasattr(ManifestValidator, "ALLOWED_CATEGORIES")
        assert isinstance(ManifestValidator.ALLOWED_CATEGORIES, list)
        assert "productivity" in ManifestValidator.ALLOWED_CATEGORIES

    def test_allowed_kinds_defined(self) -> None:
        """Test that ALLOWED_KINDS constant is defined."""
        assert hasattr(ManifestValidator, "ALLOWED_KINDS")
        assert isinstance(ManifestValidator.ALLOWED_KINDS, list)
        assert "workflow" in ManifestValidator.ALLOWED_KINDS
        assert "tool" in ManifestValidator.ALLOWED_KINDS
        assert "agent" in ManifestValidator.ALLOWED_KINDS


class TestManifestValidatorValidate:
    """Test ManifestValidator.validate() method."""

    @pytest.fixture
    def validator(self) -> ManifestValidator:
        """Create a ManifestValidator instance."""
        return ManifestValidator()

    @pytest.fixture
    def valid_manifest_data(self) -> dict[str, Any]:
        """Create valid manifest data."""
        return {
            "name": "test-automation",
            "title": "Test Automation",
            "version": "1.0.0",
            "description": "A test automation for validation testing purposes",
            "kind": "workflow",
            "category": "general",
            "base_price": 1.0,
            "input_schema": {
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"],
            },
            "output_schema": {
                "type": "object",
                "properties": {"result": {"type": "string"}},
            },
        }

    def test_validate_valid_manifest_file(
        self, validator: ManifestValidator, valid_manifest_data: dict[str, Any]
    ) -> None:
        """Test validating a valid manifest file."""
        with tempfile.TemporaryDirectory() as temp_dir:
            manifest_path = Path(temp_dir) / "automation.yaml"
            import yaml

            with open(manifest_path, "w") as f:
                yaml.dump(valid_manifest_data, f)

            result = validator.validate(manifest_path)
            # Should have no errors (may have warnings/info)
            assert len(result.errors) == 0

    def test_validate_nonexistent_file(self, validator: ManifestValidator) -> None:
        """Test validating a non-existent file returns error."""
        result = validator.validate("/nonexistent/path/automation.yaml")
        assert not result.valid
        assert len(result.errors) > 0
        assert any("not found" in e.message.lower() for e in result.errors)

    def test_validate_directory_with_manifest(
        self, validator: ManifestValidator, valid_manifest_data: dict[str, Any]
    ) -> None:
        """Test validating a directory containing automation.yaml."""
        with tempfile.TemporaryDirectory() as temp_dir:
            manifest_path = Path(temp_dir) / "automation.yaml"
            import yaml

            with open(manifest_path, "w") as f:
                yaml.dump(valid_manifest_data, f)

            # Pass directory, not file
            result = validator.validate(temp_dir)
            assert len(result.errors) == 0


class TestManifestValidatorSchemaValidation:
    """Test ManifestValidator schema validation methods."""

    @pytest.fixture
    def validator(self) -> ManifestValidator:
        """Create a ManifestValidator instance."""
        return ManifestValidator()

    def test_validate_input_schema_valid(self, validator: ManifestValidator) -> None:
        """Test validating a valid input schema."""
        schema = {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "limit": {"type": "integer", "minimum": 1},
            },
            "required": ["query"],
        }
        errors = validator.validate_input_schema(schema)
        assert len(errors) == 0

    def test_validate_input_schema_missing_type(
        self, validator: ManifestValidator
    ) -> None:
        """Test validating schema without type property."""
        schema = {"properties": {"query": {"type": "string"}}}
        errors = validator.validate_input_schema(schema)
        assert len(errors) > 0
        assert any("type" in e.lower() for e in errors)

    def test_validate_input_schema_invalid_required(
        self, validator: ManifestValidator
    ) -> None:
        """Test validating schema with required field not in properties."""
        schema = {
            "type": "object",
            "properties": {"query": {"type": "string"}},
            "required": ["nonexistent"],
        }
        errors = validator.validate_input_schema(schema)
        assert len(errors) > 0
        assert any("nonexistent" in e for e in errors)

    def test_validate_output_schema_valid(self, validator: ManifestValidator) -> None:
        """Test validating a valid output schema."""
        schema = {
            "type": "object",
            "properties": {
                "results": {"type": "array", "items": {"type": "string"}},
                "total": {"type": "integer"},
            },
        }
        errors = validator.validate_output_schema(schema)
        assert len(errors) == 0

    def test_validate_output_schema_array_without_items(
        self, validator: ManifestValidator
    ) -> None:
        """Test validating array schema without items definition."""
        schema = {
            "type": "array",
        }
        errors = validator.validate_output_schema(schema)
        assert len(errors) > 0
        assert any("items" in e.lower() for e in errors)

    def test_validate_schema_nested_objects(self, validator: ManifestValidator) -> None:
        """Test validating deeply nested schema."""
        schema = {
            "type": "object",
            "properties": {
                "data": {
                    "type": "object",
                    "properties": {
                        "nested": {
                            "type": "object",
                            "properties": {"value": {"type": "string"}},
                        }
                    },
                }
            },
        }
        errors = validator.validate_input_schema(schema)
        assert len(errors) == 0


# =============================================================================
# SecurityValidator Tests
# =============================================================================


class TestSecurityValidatorConstants:
    """Test SecurityValidator constants and configuration."""

    def test_allowed_imports_defined(self) -> None:
        """Test that ALLOWED_IMPORTS constant is defined."""
        assert hasattr(SecurityValidator, "ALLOWED_IMPORTS")
        assert isinstance(SecurityValidator.ALLOWED_IMPORTS, set)
        assert len(SecurityValidator.ALLOWED_IMPORTS) > 0

    def test_allowed_imports_contains_expected(self) -> None:
        """Test ALLOWED_IMPORTS contains expected modules."""
        allowed = SecurityValidator.ALLOWED_IMPORTS
        # Standard library
        assert "typing" in allowed
        assert "json" in allowed
        assert "datetime" in allowed
        # SDK
        assert "torivers_sdk" in allowed
        # Third-party
        assert "pydantic" in allowed
        # httpx and aiohttp are NOT allowed (proxy bypass prevention)
        assert "httpx" not in allowed
        assert "aiohttp" not in allowed

    def test_blocked_functions_defined(self) -> None:
        """Test that BLOCKED_FUNCTIONS constant is defined."""
        assert hasattr(SecurityValidator, "BLOCKED_FUNCTIONS")
        assert isinstance(SecurityValidator.BLOCKED_FUNCTIONS, set)
        assert "eval" in SecurityValidator.BLOCKED_FUNCTIONS
        assert "exec" in SecurityValidator.BLOCKED_FUNCTIONS

    def test_forbidden_modules_defined(self) -> None:
        """Test that FORBIDDEN_MODULES constant is defined."""
        assert hasattr(SecurityValidator, "FORBIDDEN_MODULES")
        assert isinstance(SecurityValidator.FORBIDDEN_MODULES, set)
        assert "os" in SecurityValidator.FORBIDDEN_MODULES
        assert "subprocess" in SecurityValidator.FORBIDDEN_MODULES

    def test_forbidden_modules_includes_code_execution(self) -> None:
        """Test FORBIDDEN_MODULES includes code/codeop/gc/_thread."""
        forbidden = SecurityValidator.FORBIDDEN_MODULES
        assert "code" in forbidden
        assert "codeop" in forbidden
        assert "gc" in forbidden
        assert "_thread" in forbidden

    def test_blocked_functions_includes_builtins(self) -> None:
        """Test BLOCKED_FUNCTIONS includes breakpoint, input, globals, locals, vars."""
        blocked = SecurityValidator.BLOCKED_FUNCTIONS
        assert "breakpoint" in blocked
        assert "input" in blocked
        assert "globals" in blocked
        assert "locals" in blocked
        assert "vars" in blocked


class TestSecurityValidatorImports:
    """Test SecurityValidator.validate_imports() method."""

    @pytest.fixture
    def validator(self) -> SecurityValidator:
        """Create a SecurityValidator instance."""
        return SecurityValidator()

    def test_validate_imports_allowed_modules(
        self, validator: SecurityValidator
    ) -> None:
        """Test that allowed imports pass validation."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
import json
import datetime
from typing import Any
from pydantic import BaseModel
from torivers_sdk.context import ExecutionContext
"""
            )

            result = validator.validate_imports(py_file)
            assert result.passed
            assert len(result.issues) == 0

    def test_validate_imports_disallowed_modules(
        self, validator: SecurityValidator
    ) -> None:
        """Test that disallowed imports fail validation."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
import os
import subprocess
"""
            )

            result = validator.validate_imports(py_file)
            assert not result.passed
            assert len(result.issues) > 0

    def test_validate_imports_rejects_prefix_collision(
        self, validator: SecurityValidator
    ) -> None:
        """Disallowed modules should not pass by prefix match (requests vs re)."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text("import requests\n")

            result = validator.validate_imports(py_file)
            assert not result.passed
            assert any("requests" in i.message for i in result.issues)

    def test_validate_imports_directory(self, validator: SecurityValidator) -> None:
        """Test validating imports in a directory."""
        with tempfile.TemporaryDirectory() as temp_dir:
            # Create multiple Python files
            (Path(temp_dir) / "main.py").write_text("import json\n")
            (Path(temp_dir) / "utils.py").write_text("import datetime\n")

            result = validator.validate_imports(temp_dir)
            assert result.passed

    def test_validate_imports_ignores_virtualenv_dir(
        self, validator: SecurityValidator
    ) -> None:
        """Virtualenv files should be ignored when scanning a directory."""
        with tempfile.TemporaryDirectory() as temp_dir:
            (Path(temp_dir) / "main.py").write_text("import json\n")
            venv_file = (
                Path(temp_dir)
                / ".venv"
                / "lib"
                / "python3.11"
                / "site-packages"
                / "bad.py"
            )
            venv_file.parent.mkdir(parents=True, exist_ok=True)
            venv_file.write_text("import os\n")

            result = validator.validate_imports(temp_dir)
            assert result.passed
            assert len(result.issues) == 0

    def test_validate_imports_new_forbidden_modules(
        self, validator: SecurityValidator
    ) -> None:
        """Test that code, codeop, gc, _thread imports are forbidden."""
        for mod in ("code", "codeop", "gc", "_thread"):
            with tempfile.TemporaryDirectory() as temp_dir:
                py_file = Path(temp_dir) / "automation.py"
                py_file.write_text(f"import {mod}\n")

                result = validator.validate_directory(temp_dir)
                assert not result.passed, f"import {mod} should be forbidden"


class TestSecurityValidatorDangerousFunctions:
    """Test SecurityValidator.check_dangerous_functions() method."""

    @pytest.fixture
    def validator(self) -> SecurityValidator:
        """Create a SecurityValidator instance."""
        return SecurityValidator()

    def test_check_dangerous_functions_eval(self, validator: SecurityValidator) -> None:
        """Test that eval() is detected."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
result = eval("1 + 1")
"""
            )

            result = validator.check_dangerous_functions(py_file)
            assert not result.passed
            assert any("eval" in i.message.lower() for i in result.issues)

    def test_check_dangerous_functions_exec(self, validator: SecurityValidator) -> None:
        """Test that exec() is detected."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
exec("print('hello')")
"""
            )

            result = validator.check_dangerous_functions(py_file)
            assert not result.passed
            assert any("exec" in i.message.lower() for i in result.issues)

    def test_check_dangerous_functions_safe_code(
        self, validator: SecurityValidator
    ) -> None:
        """Test that safe code passes."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
def process_data(data: dict) -> dict:
    return {"result": data.get("input", "")}
"""
            )

            result = validator.check_dangerous_functions(py_file)
            assert result.passed

    def test_check_blocked_functions_breakpoint(
        self, validator: SecurityValidator
    ) -> None:
        """Test that breakpoint() is detected via AST enforcement."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text("breakpoint()\n")

            result = validator.check_dangerous_functions(py_file)
            assert not result.passed
            assert any("breakpoint" in i.message for i in result.issues)

    def test_check_blocked_functions_globals(
        self, validator: SecurityValidator
    ) -> None:
        """Test that globals() is detected via AST enforcement."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text("x = globals()\n")

            result = validator.check_dangerous_functions(py_file)
            assert not result.passed
            assert any("globals" in i.message for i in result.issues)

    def test_check_blocked_dotted_function_os_system(
        self, validator: SecurityValidator
    ) -> None:
        """Test that os.system() is detected via AST attribute check."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text("import os\nos.system('ls')\n")

            result = validator.check_dangerous_functions(py_file)
            assert not result.passed
            assert any("os.system" in i.message for i in result.issues)

    def test_no_false_skip_on_test_substring(
        self, validator: SecurityValidator
    ) -> None:
        """Files like 'latest.py' should NOT be skipped by test-file logic."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "latest.py"
            py_file.write_text("x = eval('1+1')\n")

            result = validator.check_dangerous_functions(temp_dir)
            assert not result.passed
            assert any("eval" in i.message.lower() for i in result.issues)


class TestSecurityValidatorSecrets:
    """Test SecurityValidator.scan_for_secrets() method."""

    @pytest.fixture
    def validator(self) -> SecurityValidator:
        """Create a SecurityValidator instance."""
        return SecurityValidator()

    def test_scan_for_secrets_api_key(self, validator: SecurityValidator) -> None:
        """Test that hardcoded API keys are detected."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
API_KEY = "sk-1234567890abcdefghijklmnop"
"""
            )

            result = validator.scan_for_secrets(py_file)
            assert not result.passed
            assert any("api" in i.message.lower() for i in result.issues)

    def test_scan_for_secrets_password(self, validator: SecurityValidator) -> None:
        """Test that hardcoded passwords are detected."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
PASSWORD = "supersecretpassword123"
"""
            )

            result = validator.scan_for_secrets(py_file)
            assert not result.passed
            assert any(
                "secret" in i.message.lower() or "password" in i.message.lower()
                for i in result.issues
            )

    def test_scan_for_secrets_private_key(self, validator: SecurityValidator) -> None:
        """Test that private keys are detected."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                '''
KEY = """-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"""
'''
            )

            result = validator.scan_for_secrets(py_file)
            assert not result.passed
            assert any("private key" in i.message.lower() for i in result.issues)

    def test_scan_for_secrets_clean_code(self, validator: SecurityValidator) -> None:
        """Test that clean code passes secret scan."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
import os

def get_api_key():
    # Get API key from environment or credentials
    return os.environ.get("API_KEY")
"""
            )

            result = validator.scan_for_secrets(py_file)
            # Note: May flag "os" import, but should not flag secret patterns
            secret_issues = [i for i in result.issues if i.code.startswith("S4")]
            assert len(secret_issues) == 0


class TestSecurityValidatorAll:
    """Test SecurityValidator.validate_all() method."""

    @pytest.fixture
    def validator(self) -> SecurityValidator:
        """Create a SecurityValidator instance."""
        return SecurityValidator()

    def test_validate_all_safe_code(self, validator: SecurityValidator) -> None:
        """Test validate_all with safe code."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
import json
from typing import Any
from pydantic import BaseModel

class InputModel(BaseModel):
    query: str

def process(data: dict[str, Any]) -> dict[str, Any]:
    return {"result": json.dumps(data)}
"""
            )

            result = validator.validate_all(py_file)
            assert result.passed
            assert result.score > 90.0

    def test_validate_all_multiple_issues(self, validator: SecurityValidator) -> None:
        """Test validate_all detects multiple issues."""
        with tempfile.TemporaryDirectory() as temp_dir:
            py_file = Path(temp_dir) / "automation.py"
            py_file.write_text(
                """
import os
import subprocess

API_KEY = "sk-1234567890abcdefghijklmnop"

result = eval(user_input)
"""
            )

            result = validator.validate_all(py_file)
            assert not result.passed
            assert len(result.issues) >= 3  # import + secret + eval
            assert result.score < 50.0

    def test_validate_all_ignores_gitignored_and_virtualenv_paths(
        self, validator: SecurityValidator
    ) -> None:
        """Directory scans should ignore .venv and .gitignore excluded paths."""
        with tempfile.TemporaryDirectory() as temp_dir:
            root = Path(temp_dir)
            (root / "main.py").write_text("import json\n")
            (root / ".gitignore").write_text(".generated/\n")

            venv_file = (
                root / ".venv" / "lib" / "python3.11" / "site-packages" / "bad.py"
            )
            venv_file.parent.mkdir(parents=True, exist_ok=True)
            venv_file.write_text("import os\nx = eval('1+1')\n")

            generated_file = root / ".generated" / "leak.py"
            generated_file.parent.mkdir(parents=True, exist_ok=True)
            generated_file.write_text('TOKEN = "super-secret-token-value"\n')

            result = validator.validate_all(root)
            assert result.passed
            assert len(result.issues) == 0


# =============================================================================
# SchemaValidator Tests
# =============================================================================


class TestSchemaValidator:
    """Test SchemaValidator class."""

    @pytest.fixture
    def validator(self) -> SchemaValidator:
        """Create a SchemaValidator instance."""
        return SchemaValidator()

    def test_validate_valid_object(self, validator: SchemaValidator) -> None:
        """Test validating a valid object."""
        schema = {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "age": {"type": "integer"},
            },
            "required": ["name"],
        }
        data = {"name": "Alice", "age": 30}

        result = validator.validate(data, schema)
        assert result.valid
        assert len(result.errors) == 0

    def test_validate_missing_required(self, validator: SchemaValidator) -> None:
        """Test validation fails for missing required field."""
        schema = {
            "type": "object",
            "properties": {"name": {"type": "string"}},
            "required": ["name"],
        }
        data = {}

        result = validator.validate(data, schema)
        assert not result.valid
        assert len(result.errors) == 1
        assert "required" in result.errors[0].message.lower()

    def test_validate_wrong_type(self, validator: SchemaValidator) -> None:
        """Test validation fails for wrong type."""
        schema = {"type": "string"}
        data = 123

        result = validator.validate(data, schema)
        assert not result.valid
        assert "string" in result.errors[0].message.lower()

    def test_validate_array_constraints(self, validator: SchemaValidator) -> None:
        """Test array min/max items validation."""
        schema = {
            "type": "array",
            "items": {"type": "string"},
            "minItems": 1,
            "maxItems": 3,
        }

        # Too few items
        result = validator.validate([], schema)
        assert not result.valid

        # Valid
        result = validator.validate(["a", "b"], schema)
        assert result.valid

        # Too many items
        result = validator.validate(["a", "b", "c", "d"], schema)
        assert not result.valid

    def test_validate_number_constraints(self, validator: SchemaValidator) -> None:
        """Test number min/max validation."""
        schema = {"type": "integer", "minimum": 0, "maximum": 100}

        # Below minimum
        result = validator.validate(-1, schema)
        assert not result.valid

        # Valid
        result = validator.validate(50, schema)
        assert result.valid

        # Above maximum
        result = validator.validate(101, schema)
        assert not result.valid

    def test_validate_string_constraints(self, validator: SchemaValidator) -> None:
        """Test string length and pattern validation."""
        schema = {
            "type": "string",
            "minLength": 3,
            "maxLength": 10,
            "pattern": "^[a-z]+$",
        }

        # Too short
        result = validator.validate("ab", schema)
        assert not result.valid

        # Too long
        result = validator.validate("abcdefghijk", schema)
        assert not result.valid

        # Wrong pattern
        result = validator.validate("ABC123", schema)
        assert not result.valid

        # Valid
        result = validator.validate("hello", schema)
        assert result.valid


class TestSchemaValidatorConvenienceMethods:
    """Test SchemaValidator convenience methods."""

    @pytest.fixture
    def validator(self) -> SchemaValidator:
        """Create a SchemaValidator instance."""
        return SchemaValidator()

    def test_validate_input(self, validator: SchemaValidator) -> None:
        """Test validate_input convenience method."""
        input_schema = {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "limit": {"type": "integer", "minimum": 1},
            },
            "required": ["query"],
        }

        # Valid input
        result = validator.validate_input(
            {"query": "search term", "limit": 10}, input_schema
        )
        assert result.valid

        # Invalid input
        result = validator.validate_input({"limit": 10}, input_schema)
        assert not result.valid

    def test_validate_output(self, validator: SchemaValidator) -> None:
        """Test validate_output convenience method."""
        output_schema = {
            "type": "object",
            "properties": {
                "results": {"type": "array", "items": {"type": "string"}},
                "total": {"type": "integer"},
            },
            "required": ["results"],
        }

        # Valid output
        result = validator.validate_output(
            {"results": ["item1", "item2"], "total": 2}, output_schema
        )
        assert result.valid

        # Invalid output
        result = validator.validate_output({"total": 2}, output_schema)
        assert not result.valid


# =============================================================================
# Result Types Tests
# =============================================================================


class TestValidationResultTypes:
    """Test validation result dataclasses."""

    def test_validation_result_to_dict(self) -> None:
        """Test ValidationResult.to_dict() method."""
        result = ValidationResult(
            valid=False,
            issues=[
                ValidationIssue(
                    level=ValidationLevel.ERROR,
                    code="E001",
                    message="Test error",
                    location="test",
                )
            ],
        )
        d = result.to_dict()
        assert d["valid"] is False
        assert d["error_count"] == 1
        assert d["warning_count"] == 0

    def test_schema_validation_result_to_dict(self) -> None:
        """Test SchemaValidationResult.to_dict() method."""
        result = SchemaValidationResult(
            valid=False,
            errors=[
                SchemaValidationError(
                    path="$.name", message="Required field", value=None
                )
            ],
        )
        d = result.to_dict()
        assert d["valid"] is False
        assert len(d["errors"]) == 1
        assert d["errors"][0]["path"] == "$.name"

    def test_security_issue_fields(self) -> None:
        """Test SecurityIssue has expected fields."""
        issue = SecurityIssue(
            severity="high",
            code="S001",
            message="Test security issue",
            file="test.py",
            line=10,
        )
        assert issue.severity == "high"
        assert issue.code == "S001"
        assert issue.message == "Test security issue"
        assert issue.file == "test.py"
        assert issue.line == 10

    def test_security_result_fields(self) -> None:
        """Test SecurityResult has expected fields."""
        result = SecurityResult(
            passed=True,
            issues=[],
            score=100.0,
        )
        assert result.passed is True
        assert result.score == 100.0
        assert len(result.issues) == 0

    def test_security_result_with_issues(self) -> None:
        """Test SecurityResult with issues."""
        issue = SecurityIssue(
            severity="critical",
            code="S400",
            message="Hardcoded secret detected",
            file="main.py",
            line=5,
        )
        result = SecurityResult(
            passed=False,
            issues=[issue],
            score=75.0,
        )
        assert not result.passed
        assert len(result.issues) == 1
        assert result.issues[0].severity == "critical"

    def test_validation_level_enum(self) -> None:
        """Test ValidationLevel enum values."""
        assert ValidationLevel.ERROR.value == "error"
        assert ValidationLevel.WARNING.value == "warning"
        assert ValidationLevel.INFO.value == "info"


# =============================================================================
# Integration Tests
# =============================================================================


class TestValidatorsIntegration:
    """Integration tests for validators working together."""

    def test_full_automation_validation(self) -> None:
        """Test validating a complete automation package."""
        with tempfile.TemporaryDirectory() as temp_dir:
            # Create manifest
            manifest_path = Path(temp_dir) / "automation.yaml"
            import yaml

            manifest_data = {
                "name": "test-automation",
                "title": "Test Automation",
                "version": "1.0.0",
                "description": "A test automation for integration testing purposes",
                "kind": "workflow",
                "category": "general",
                "base_price": 1.0,
                "input_schema": {
                    "type": "object",
                    "properties": {"query": {"type": "string"}},
                    "required": ["query"],
                },
                "output_schema": {
                    "type": "object",
                    "properties": {"result": {"type": "string"}},
                },
            }
            with open(manifest_path, "w") as f:
                yaml.dump(manifest_data, f)

            # Create automation code
            automation_path = Path(temp_dir) / "automation.py"
            automation_path.write_text(
                """
import json
from typing import Any
from pydantic import BaseModel

class Automation:
    def execute(self, input_data: dict[str, Any]) -> dict[str, Any]:
        query = input_data.get("query", "")
        return {"result": f"Processed: {query}"}
"""
            )

            # Validate manifest
            manifest_validator = ManifestValidator()
            manifest_result = manifest_validator.validate(manifest_path)
            assert len(manifest_result.errors) == 0

            # Validate security
            security_validator = SecurityValidator()
            security_result = security_validator.validate_all(temp_dir)
            assert security_result.passed

            # Validate input/output schemas
            schema_validator = SchemaValidator()

            input_result = schema_validator.validate_input(
                {"query": "test"}, manifest_data["input_schema"]
            )
            assert input_result.valid

            output_result = schema_validator.validate_output(
                {"result": "test"}, manifest_data["output_schema"]
            )
            assert output_result.valid
