"""Tests for type_converter module."""

from re import A
import pytest
from typing import Literal, get_args, get_origin
from pydantic import BaseModel
from lamia.type_converter import create_validator
from lamia.types import HTML, YAML, JSON, XML, CSV, Markdown, BaseModel as BaseModelType, BaseType
from lamia.validation.base import BaseValidator
from lamia.validation.validators.file_validators.html_validator import HTMLValidator
from lamia.validation.validators.file_validators.yaml_validator import YAMLValidator
from lamia.validation.validators.file_validators.json_validator import JSONValidator
from lamia.validation.validators.file_validators.xml_validator import XMLValidator
from lamia.validation.validators.file_validators.csv_validator import CSVValidator
from lamia.validation.validators.file_validators.markdown_validator import MarkdownValidator
from lamia.validation.validators.object_validator import ObjectValidator
from lamia.validation.validators.file_validators.file_structure.html_structure_validator import HTMLStructureValidator
from lamia.validation.validators.file_validators.file_structure.yaml_structure_validator import YAMLStructureValidator
from lamia.validation.validators.file_validators.file_structure.json_structure_validator import JSONStructureValidator
from lamia.validation.validators.file_validators.file_structure.xml_structure_validator import XMLStructureValidator
from lamia.validation.validators.file_validators.file_structure.csv_structure_validator import CSVStructureValidator
from lamia.validation.validators.file_validators.file_structure.markdown_structure_validator import (
    MarkdownStructureValidator,
    Heading1,
    Paragraph,
)


class SimpleTestModel(BaseModel):
    """Simple test model for parametric type tests."""
    name: str
    age: int


class MarkdownTestModel(BaseModel):
    """Test model using markdown types."""
    title: Heading1
    content: Paragraph


class TestBasicTypes:
    """Test basic type validators (non-parametric)."""

    def test_html_basic_type(self):
        """Test HTML basic type returns HTMLValidator."""
        validator = create_validator(HTML)
        assert isinstance(validator, HTMLValidator)
        assert isinstance(validator, BaseValidator)

    def test_yaml_basic_type(self):
        """Test YAML basic type returns YAMLValidator."""
        validator = create_validator(YAML)
        assert isinstance(validator, YAMLValidator)
        assert isinstance(validator, BaseValidator)

    def test_json_basic_type(self):
        """Test JSON basic type returns JSONValidator."""
        validator = create_validator(JSON)
        assert isinstance(validator, JSONValidator)
        assert isinstance(validator, BaseValidator)

    def test_xml_basic_type(self):
        """Test XML basic type returns XMLValidator."""
        validator = create_validator(XML)
        assert isinstance(validator, XMLValidator)
        assert isinstance(validator, BaseValidator)

    def test_csv_basic_type(self):
        """Test CSV basic type returns CSVValidator."""
        validator = create_validator(CSV)
        assert isinstance(validator, CSVValidator)
        assert isinstance(validator, BaseValidator)

    def test_markdown_basic_type(self):
        """Test Markdown basic type returns MarkdownValidator."""
        validator = create_validator(Markdown)
        assert isinstance(validator, MarkdownValidator)
        assert isinstance(validator, BaseValidator)


class TestParametricTypes:
    """Test parametric types with single model argument."""

    def test_html_parametric_type(self):
        """Test HTML[Model] returns HTMLStructureValidator."""
        validator = create_validator(HTML[SimpleTestModel])
        assert isinstance(validator, HTMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_yaml_parametric_type(self):
        """Test YAML[Model] returns YAMLStructureValidator."""
        validator = create_validator(YAML[SimpleTestModel])
        assert isinstance(validator, YAMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_json_parametric_type(self):
        """Test JSON[Model] returns JSONStructureValidator."""
        validator = create_validator(JSON[SimpleTestModel])
        assert isinstance(validator, JSONStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_xml_parametric_type(self):
        """Test XML[Model] returns XMLStructureValidator."""
        validator = create_validator(XML[SimpleTestModel])
        assert isinstance(validator, XMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_csv_parametric_type(self):
        """Test CSV[Model] returns CSVStructureValidator."""
        validator = create_validator(CSV[SimpleTestModel])
        assert isinstance(validator, CSVStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_markdown_parametric_type(self):
        """Test Markdown[Model] returns MarkdownStructureValidator."""
        validator = create_validator(Markdown[MarkdownTestModel])
        assert isinstance(validator, MarkdownStructureValidator)
        assert isinstance(validator, BaseValidator)


class TestTwoArgParametricTypes:
    """Test parametric types with two arguments (model and strict)."""

    def test_html_with_strict_true(self):
        """Test HTML[Model, True] returns HTMLStructureValidator with strict=True."""
        validator = create_validator(HTML[SimpleTestModel, True])
        assert isinstance(validator, HTMLStructureValidator)
        assert validator.strict == True
        assert isinstance(validator, BaseValidator)

    def test_html_with_strict_false(self):
        """Test HTML[Model, False] returns HTMLStructureValidator with strict=False."""
        validator = create_validator(HTML[SimpleTestModel, False])
        assert isinstance(validator, HTMLStructureValidator)
        assert validator.strict == False
        assert isinstance(validator, BaseValidator)

    def test_yaml_with_strict_true(self):
        """Test YAML[Model, True] returns YAMLStructureValidator with strict=True."""
        validator = create_validator(YAML[SimpleTestModel, True])
        assert isinstance(validator, YAMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_yaml_with_strict_false(self):
        """Test YAML[Model, False] returns YAMLStructureValidator with strict=False."""
        validator = create_validator(YAML[SimpleTestModel, False])
        assert isinstance(validator, YAMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_json_with_strict_true(self):
        """Test JSON[Model, True] returns JSONStructureValidator with strict=True."""
        validator = create_validator(JSON[SimpleTestModel, True])
        assert isinstance(validator, JSONStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_json_with_strict_false(self):
        """Test JSON[Model, False] returns JSONStructureValidator with strict=False."""
        validator = create_validator(JSON[SimpleTestModel, False])
        assert isinstance(validator, JSONStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_xml_with_strict_true(self):
        """Test XML[Model, True] returns XMLStructureValidator with strict=True."""
        validator = create_validator(XML[SimpleTestModel, True])
        assert isinstance(validator, XMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_xml_with_strict_false(self):
        """Test XML[Model, False] returns XMLStructureValidator with strict=False."""
        validator = create_validator(XML[SimpleTestModel, False])
        assert isinstance(validator, XMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_csv_with_strict_true(self):
        """Test CSV[Model, True] returns CSVStructureValidator with strict=True."""
        validator = create_validator(CSV[SimpleTestModel, True])
        assert isinstance(validator, CSVStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_csv_with_strict_false(self):
        """Test CSV[Model, False] returns CSVStructureValidator with strict=False."""
        validator = create_validator(CSV[SimpleTestModel, False])
        assert isinstance(validator, CSVStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_markdown_with_strict_true(self):
        """Test Markdown[Model, True] returns MarkdownStructureValidator with strict=True."""
        validator = create_validator(Markdown[MarkdownTestModel, True])
        assert isinstance(validator, MarkdownStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_markdown_with_strict_false(self):
        """Test Markdown[Model, False] returns MarkdownStructureValidator with strict=False."""
        validator = create_validator(Markdown[MarkdownTestModel, False])
        assert isinstance(validator, MarkdownStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_with_bool_class(self):
        """Test that passing bool class defaults to strict=True."""
        validator = create_validator(HTML[SimpleTestModel, bool])
        assert isinstance(validator, HTMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_with_literal_true(self):
        """Test that Literal[True] works for strict parameter."""
        validator = create_validator(HTML[SimpleTestModel, Literal[True]])
        assert isinstance(validator, HTMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_with_literal_false(self):
        """Test that Literal[False] works for strict parameter."""
        validator = create_validator(HTML[SimpleTestModel, Literal[False]])
        assert isinstance(validator, HTMLStructureValidator)
        assert isinstance(validator, BaseValidator)


class TestInvalidTypes:
    """Test invalid types raise ValueError."""

    def test_invalid_type_with_too_many_args(self):
        """Test that types with more than 2 args raise ValueError."""
        # Since BaseType only supports 2 type parameters, Python's type system prevents
        # creating types with 3 args. However, we can test the error path by mocking.
        from unittest.mock import Mock, patch
        
        # Create a mock type that simulates having 3 args
        # The function checks get_origin first, then get_args
        mock_type = Mock()
        mock_type.__origin__ = HTML
        
        # Patch both get_origin and get_args to simulate a type with 3 args
        def mock_get_origin(tp):
            if tp is mock_type:
                return HTML
            return get_origin(tp)
        
        def mock_get_args(tp):
            if tp is mock_type:
                return (SimpleTestModel, True, "extra")
            return get_args(tp)
        
        with patch('lamia.type_converter.get_origin', side_effect=mock_get_origin):
            with patch('lamia.type_converter.get_args', side_effect=mock_get_args):
                with pytest.raises(ValueError, match="Invalid type"):
                    create_validator(mock_type)

    def test_unsupported_base_type(self):
        """Test that unsupported base types raise ValueError."""
        class UnsupportedType(BaseType):
            pass
        
        with pytest.raises(ValueError, match="Unsupported validation type"):
            create_validator(UnsupportedType)

    def test_invalid_literal_type(self):
        """Test that invalid Literal types raise ValueError."""
        with pytest.raises(ValueError, match="Invalid literal type for strict parameter"):
            create_validator(HTML[SimpleTestModel, Literal["invalid"]])


class TestGenerateHints:
    """Test generate_hints parameter is passed through."""

    def test_html_basic_with_generate_hints_true(self):
        """Test HTML basic type with generate_hints=True."""
        validator = create_validator(HTML, generate_hints=True)
        assert isinstance(validator, HTMLValidator)
        assert isinstance(validator, BaseValidator)

    def test_html_basic_with_generate_hints_false(self):
        """Test HTML basic type with generate_hints=False."""
        validator = create_validator(HTML, generate_hints=False)
        assert isinstance(validator, HTMLValidator)
        assert isinstance(validator, BaseValidator)

    def test_html_parametric_with_generate_hints_true(self):
        """Test HTML[Model] with generate_hints=True."""
        validator = create_validator(HTML[SimpleTestModel], generate_hints=True)
        assert isinstance(validator, HTMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_html_parametric_with_generate_hints_false(self):
        """Test HTML[Model] with generate_hints=False."""
        validator = create_validator(HTML[SimpleTestModel], generate_hints=False)
        assert isinstance(validator, HTMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_html_two_arg_with_generate_hints_true(self):
        """Test HTML[Model, True] with generate_hints=True."""
        validator = create_validator(HTML[SimpleTestModel, True], generate_hints=True)
        assert isinstance(validator, HTMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_yaml_with_generate_hints_true(self):
        """Test YAML[Model] with generate_hints=True."""
        validator = create_validator(YAML[SimpleTestModel], generate_hints=True)
        assert isinstance(validator, YAMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_json_with_generate_hints_true(self):
        """Test JSON[Model] with generate_hints=True."""
        validator = create_validator(JSON[SimpleTestModel], generate_hints=True)
        assert isinstance(validator, JSONStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_xml_with_generate_hints_true(self):
        """Test XML[Model] with generate_hints=True."""
        validator = create_validator(XML[SimpleTestModel], generate_hints=True)
        assert isinstance(validator, XMLStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_csv_with_generate_hints_true(self):
        """Test CSV[Model] with generate_hints=True."""
        validator = create_validator(CSV[SimpleTestModel], generate_hints=True)
        assert isinstance(validator, CSVStructureValidator)
        assert isinstance(validator, BaseValidator)

    def test_markdown_with_generate_hints_true(self):
        """Test Markdown[Model] with generate_hints=True."""
        validator = create_validator(Markdown[MarkdownTestModel], generate_hints=True)
        assert isinstance(validator, MarkdownStructureValidator)
        assert isinstance(validator, BaseValidator)


class TestBaseModelType:
    """Test BaseModel type handling."""

    def test_base_model_type(self):
        """Test BaseModel returns ObjectValidator."""
        # Note: BaseModel from pydantic doesn't support type parameters
        # The code checks if base_type is BaseModel, so we test with BaseModel itself
        # However, ObjectValidator requires a schema parameter, so this will fail
        # This tests the actual behavior of the code
        with pytest.raises(TypeError, match="missing 1 required positional argument"):
            create_validator(BaseModelType)

    def test_base_model_requires_schema(self):
        """Test that BaseModel type requires schema parameter in ObjectValidator."""
        # The current implementation has a bug - it doesn't pass model to ObjectValidator
        # This test documents the current behavior
        with pytest.raises(TypeError, match="missing 1 required positional argument"):
            create_validator(BaseModelType)


class TestStrictDefaultBehavior:
    """Test strict parameter default behavior."""

    def test_basic_type_defaults_to_strict_true(self):
        """Test that basic types default to strict=True."""
        validator = create_validator(HTML)
        assert isinstance(validator, HTMLValidator)
        assert isinstance(validator, BaseValidator)

    def test_parametric_type_defaults_to_strict_false(self):
        """Test that parametric types with one arg default to strict=False."""
        validator = create_validator(HTML[SimpleTestModel])
        assert isinstance(validator, HTMLStructureValidator)
        assert isinstance(validator, BaseValidator)
