"""Tests for the schema inferencer."""

import json
import tempfile
from pathlib import Path

import pytest

from schemai.inferencer import SchemaInferencer


@pytest.fixture
def inferencer():
    """Create a schema inferencer for testing."""
    return SchemaInferencer()


@pytest.fixture
def sample_json_file():
    """Create a temporary JSON file for testing."""
    data = {
        "name": "John Doe",
        "age": 30,
        "email": "john@example.com",
        "is_active": True,
        "balance": 1000.50,
        "tags": ["admin", "user"],
    }
    with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
        json.dump(data, f)
        yield f.name
    Path(f.name).unlink()


class TestSchemaInferencer:
    """Test cases for SchemaInferencer."""

    def test_infer_basic_types(self, inferencer):
        """Test inference of basic Python types."""
        assert inferencer._infer_type(None)[0] == str
        assert inferencer._infer_type(True)[0] == bool
        assert inferencer._infer_type(123)[0] == int
        assert inferencer._infer_type(123.45)[0] == float
        assert inferencer._infer_type("hello")[0] == str

    def test_infer_list_type(self, inferencer):
        """Test inference of list types."""
        result_type, _ = inferencer._infer_type([1, 2, 3])
        assert hasattr(result_type, "__origin__")  # It's a generic type

    def test_infer_dict_type(self, inferencer):
        """Test inference of dict types."""
        result_type, _ = inferencer._infer_type({"key": "value"})
        assert result_type == dict

    def test_infer_from_json_file(self, inferencer, sample_json_file):
        """Test full schema inference from JSON file."""
        model = inferencer.infer_from_json(sample_json_file, model_name="TestModel")
        assert model.__name__ == "TestModel"
        assert "name" in model.model_fields
        assert "age" in model.model_fields
        assert "email" in model.model_fields
        assert "is_active" in model.model_fields

    def test_infer_schema_from_dict(self, inferencer):
        """Test schema inference from a dictionary."""
        data = {"name": "Alice", "age": 25, "active": True}
        schema = inferencer._infer_schema_from_dict(data)
        
        assert "name" in schema
        assert "age" in schema
        assert "active" in schema

    def test_generate_code(self, inferencer, sample_json_file):
        """Test Python code generation."""
        model = inferencer.infer_from_json(sample_json_file)
        code = inferencer.to_code(model, class_name="User")
        
        assert "class User(BaseModel)" in code
        assert "name" in code
        assert "age" in code
        assert "from pydantic import BaseModel" in code

    def test_infer_from_json_list(self, inferencer):
        """Test inference from JSON list of objects."""
        data = [
            {"id": 1, "name": "Item 1"},
            {"id": 2, "name": "Item 2"},
        ]
        
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump(data, f)
            temp_path = f.name
        
        try:
            model = inferencer.infer_from_json(temp_path, model_name="Item")
            assert model.__name__ == "Item"
            assert "id" in model.model_fields
            assert "name" in model.model_fields
        finally:
            Path(temp_path).unlink()

    def test_error_on_nonexistent_file(self, inferencer):
        """Test error handling for nonexistent files."""
        with pytest.raises(FileNotFoundError):
            inferencer.infer_from_json("/nonexistent/file.json")

    def test_error_on_empty_json_list(self, inferencer):
        """Test error handling for empty JSON lists."""
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
            json.dump([], f)
            temp_path = f.name
        
        try:
            with pytest.raises(ValueError, match="empty list"):
                inferencer.infer_from_json(temp_path)
        finally:
            Path(temp_path).unlink()


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
