"""
Tests for chainable transformers.
"""

import pytest

from pycharter.etl_generator.transformers import (
    AddField,
    BaseTransformer,
    Convert,
    CustomFunction,
    Default,
    Drop,
    Filter,
    FlatMap,
    Map,
    Rename,
    Select,
    TransformerChain,
)


class TestRename:
    """Tests for Rename transformer."""

    def test_rename_single_field(self):
        """Test renaming a single field."""
        transformer = Rename({"old_name": "new_name"})
        data = [{"old_name": "value", "other": 1}]

        result = transformer.transform(data)

        assert result == [{"new_name": "value", "other": 1}]

    def test_rename_multiple_fields(self):
        """Test renaming multiple fields."""
        transformer = Rename({"a": "x", "b": "y"})
        data = [{"a": 1, "b": 2, "c": 3}]

        result = transformer.transform(data)

        assert result == [{"x": 1, "y": 2, "c": 3}]

    def test_rename_preserves_unmapped(self):
        """Test that unmapped fields are preserved."""
        transformer = Rename({"a": "x"})
        data = [{"a": 1, "b": 2}]

        result = transformer.transform(data)

        assert "b" in result[0]
        assert result[0]["b"] == 2


class TestAddField:
    """Tests for AddField transformer."""

    def test_add_static_value(self):
        """Test adding a static value."""
        transformer = AddField("status", "active")
        data = [{"name": "test"}]

        result = transformer.transform(data)

        assert result[0]["status"] == "active"

    def test_add_dynamic_value(self):
        """Test adding a dynamic value from function."""
        transformer = AddField("full", lambda r: f"{r['first']} {r['last']}")
        data = [{"first": "John", "last": "Doe"}]

        result = transformer.transform(data)

        assert result[0]["full"] == "John Doe"


class TestDrop:
    """Tests for Drop transformer."""

    def test_drop_single_field(self):
        """Test dropping a single field."""
        transformer = Drop(["temp"])
        data = [{"name": "test", "temp": 123}]

        result = transformer.transform(data)

        assert "temp" not in result[0]
        assert "name" in result[0]

    def test_drop_multiple_fields(self):
        """Test dropping multiple fields."""
        transformer = Drop(["a", "b"])
        data = [{"a": 1, "b": 2, "c": 3}]

        result = transformer.transform(data)

        assert result == [{"c": 3}]


class TestSelect:
    """Tests for Select transformer."""

    def test_select_fields(self):
        """Test selecting specific fields."""
        transformer = Select(["name", "email"])
        data = [{"name": "test", "email": "a@b.com", "temp": 123}]

        result = transformer.transform(data)

        assert result == [{"name": "test", "email": "a@b.com"}]


class TestFilter:
    """Tests for Filter transformer."""

    def test_filter_records(self):
        """Test filtering records."""
        transformer = Filter(lambda r: r["active"])
        data = [
            {"name": "a", "active": True},
            {"name": "b", "active": False},
            {"name": "c", "active": True},
        ]

        result = transformer.transform(data)

        assert len(result) == 2
        assert all(r["active"] for r in result)

    def test_filter_with_comparison(self):
        """Test filtering with comparison."""
        transformer = Filter(lambda r: r.get("score", 0) > 50)
        data = [
            {"name": "a", "score": 30},
            {"name": "b", "score": 70},
            {"name": "c", "score": 90},
        ]

        result = transformer.transform(data)

        assert len(result) == 2


class TestConvert:
    """Tests for Convert transformer."""

    def test_convert_types(self):
        """Test type conversion."""
        transformer = Convert({"age": int, "price": float})
        data = [{"age": "25", "price": "19.99"}]

        result = transformer.transform(data)

        assert result[0]["age"] == 25
        assert result[0]["price"] == 19.99

    def test_convert_error_ignore(self):
        """Test conversion error handling (ignore)."""
        transformer = Convert({"num": int}, errors="ignore")
        data = [{"num": "not_a_number"}]

        result = transformer.transform(data)

        assert result[0]["num"] == "not_a_number"  # Unchanged

    def test_convert_error_null(self):
        """Test conversion error handling (null)."""
        transformer = Convert({"num": int}, errors="null")
        data = [{"num": "not_a_number"}]

        result = transformer.transform(data)

        assert result[0]["num"] is None


class TestDefault:
    """Tests for Default transformer."""

    def test_default_missing_field(self):
        """Test default for missing field."""
        transformer = Default({"status": "pending"})
        data = [{"name": "test"}]

        result = transformer.transform(data)

        assert result[0]["status"] == "pending"

    def test_default_null_field(self):
        """Test default for null field."""
        transformer = Default({"status": "pending"}, replace_null=True)
        data = [{"name": "test", "status": None}]

        result = transformer.transform(data)

        assert result[0]["status"] == "pending"

    def test_default_preserves_existing(self):
        """Test default doesn't override existing."""
        transformer = Default({"status": "pending"})
        data = [{"name": "test", "status": "active"}]

        result = transformer.transform(data)

        assert result[0]["status"] == "active"


class TestMap:
    """Tests for Map transformer."""

    def test_map_function(self):
        """Test mapping with function."""

        def enrich(r):
            r["enriched"] = True
            return r

        transformer = Map(enrich)
        data = [{"name": "test"}]

        result = transformer.transform(data)

        assert result[0]["enriched"] is True


class TestFlatMap:
    """Tests for FlatMap transformer."""

    def test_flatmap_explode(self):
        """Test flatmap explodes records."""

        def explode_items(r):
            return [{"item": item, "order": r["id"]} for item in r["items"]]

        transformer = FlatMap(explode_items)
        data = [{"id": 1, "items": ["a", "b", "c"]}]

        result = transformer.transform(data)

        assert len(result) == 3
        assert result[0] == {"item": "a", "order": 1}


class TestTransformerChaining:
    """Tests for chaining transformers with | operator."""

    def test_chain_two_transformers(self):
        """Test chaining two transformers."""
        chain = Rename({"a": "x"}) | AddField("y", "value")

        assert isinstance(chain, TransformerChain)
        assert len(chain) == 2

    def test_chain_multiple_transformers(self):
        """Test chaining multiple transformers."""
        chain = Rename({"a": "x"}) | AddField("y", "value") | Drop(["z"])

        assert len(chain) == 3

    def test_chain_transforms_data(self):
        """Test chain transforms data correctly."""
        chain = Rename({"a": "x"}) | AddField("y", "default")
        data = [{"a": 1, "b": 2}]

        result = chain.transform(data)

        assert result == [{"x": 1, "b": 2, "y": "default"}]

    def test_chain_iteration(self):
        """Test chain is iterable."""
        chain = Rename({}) | Filter(lambda x: True)

        transformers = list(chain)
        assert len(transformers) == 2


class TestCustomFunction:
    """Tests for CustomFunction transformer."""

    def test_direct_callable(self):
        """Test with direct callable."""

        def my_func(data, **kwargs):
            return [{"processed": True, **r} for r in data]

        transformer = CustomFunction(func=my_func)
        data = [{"name": "test"}]

        result = transformer.transform(data)

        assert result[0]["processed"] is True

    def test_with_kwargs(self):
        """Test passing kwargs."""

        def my_func(data, prefix=""):
            return [{**r, "name": prefix + r["name"]} for r in data]

        transformer = CustomFunction(func=my_func, kwargs={"prefix": "PREFIX_"})
        data = [{"name": "test"}]

        result = transformer.transform(data)

        assert result[0]["name"] == "PREFIX_test"

    def test_missing_args_raises(self):
        """Test missing args raises error."""
        with pytest.raises(ValueError, match="Must provide"):
            CustomFunction()
