"""
Extended tests for ExporterRegistry.

Covers previously untested functionality:
- clear() method (zero existing tests)
- Instance cache invalidation after clear
- Re-registration after clear
- Edge cases: register without format_name, duplicate formats
- get_available_formats ordering behavior
- is_format_supported after mutations
"""

from local_deep_research.exporters.base import BaseExporter, ExportResult
from local_deep_research.exporters.registry import ExporterRegistry


def _make_test_exporter(
    format_name, file_ext=".test", mime="application/x-test"
):
    """Create a concrete BaseExporter subclass with the given format name."""

    class _TestExporter(BaseExporter):
        @property
        def format_name(self) -> str:
            return format_name

        @property
        def file_extension(self) -> str:
            return file_ext

        @property
        def mimetype(self) -> str:
            return mime

        def export(self, markdown_content, options=None):
            return ExportResult(
                content=markdown_content.encode(),
                filename=f"test{file_ext}",
                mimetype=mime,
            )

    return _TestExporter


class TestExporterRegistryClear:
    """Tests for clear() method (lines 103-110)."""

    def setup_method(self):
        """Snapshot registry state before test."""
        self._orig_exporters = dict(ExporterRegistry._exporters)
        self._orig_instances = dict(ExporterRegistry._instances)

    def teardown_method(self):
        """Restore registry state after test."""
        ExporterRegistry._exporters.clear()
        ExporterRegistry._exporters.update(self._orig_exporters)
        ExporterRegistry._instances.clear()
        ExporterRegistry._instances.update(self._orig_instances)

    def test_clear_removes_all_exporters(self):
        """clear() empties the _exporters dict."""
        # Ensure at least one exporter is registered
        TestExporter = _make_test_exporter("clear_test_fmt")
        ExporterRegistry.register(TestExporter)
        assert len(ExporterRegistry._exporters) > 0

        ExporterRegistry.clear()

        assert ExporterRegistry._exporters == {}

    def test_clear_removes_all_instances(self):
        """clear() empties the _instances cache."""
        # Trigger instance creation
        TestExporter = _make_test_exporter("clear_inst_fmt")
        ExporterRegistry.register(TestExporter)
        ExporterRegistry.get_exporter("clear_inst_fmt")
        assert "clear_inst_fmt" in ExporterRegistry._instances

        ExporterRegistry.clear()

        assert ExporterRegistry._instances == {}

    def test_clear_then_get_available_formats_empty(self):
        """After clear, get_available_formats returns empty list."""
        ExporterRegistry.clear()

        assert ExporterRegistry.get_available_formats() == []

    def test_clear_then_is_format_supported_false(self):
        """After clear, is_format_supported returns False for all."""
        ExporterRegistry.clear()

        assert ExporterRegistry.is_format_supported("pdf") is False
        assert ExporterRegistry.is_format_supported("odt") is False

    def test_clear_then_get_exporter_returns_none(self):
        """After clear, get_exporter returns None."""
        ExporterRegistry.clear()

        assert ExporterRegistry.get_exporter("pdf") is None

    def test_clear_is_idempotent(self):
        """Calling clear twice does not raise."""
        ExporterRegistry.clear()
        ExporterRegistry.clear()

        assert ExporterRegistry.get_available_formats() == []

    def test_register_after_clear(self):
        """Can register a new exporter after clear."""
        ExporterRegistry.clear()

        TestExporter = _make_test_exporter("after_clear_fmt")
        ExporterRegistry.register(TestExporter)

        assert ExporterRegistry.is_format_supported("after_clear_fmt")
        exporter = ExporterRegistry.get_exporter("after_clear_fmt")
        assert exporter is not None
        assert exporter.format_name == "after_clear_fmt"

    def test_clear_does_not_affect_class_structure(self):
        """Clear only empties dicts, doesn't remove class attributes."""
        ExporterRegistry.clear()

        assert hasattr(ExporterRegistry, "_exporters")
        assert hasattr(ExporterRegistry, "_instances")
        assert isinstance(ExporterRegistry._exporters, dict)
        assert isinstance(ExporterRegistry._instances, dict)


class TestExporterRegistryInstanceCache:
    """Tests for instance caching behavior in get_exporter."""

    def setup_method(self):
        self._orig_exporters = dict(ExporterRegistry._exporters)
        self._orig_instances = dict(ExporterRegistry._instances)

    def teardown_method(self):
        ExporterRegistry._exporters.clear()
        ExporterRegistry._exporters.update(self._orig_exporters)
        ExporterRegistry._instances.clear()
        ExporterRegistry._instances.update(self._orig_instances)

    def test_get_exporter_caches_instance(self):
        """Second call to get_exporter returns cached instance."""
        TestExporter = _make_test_exporter("cache_fmt")
        ExporterRegistry.register(TestExporter)

        first = ExporterRegistry.get_exporter("cache_fmt")
        second = ExporterRegistry.get_exporter("cache_fmt")

        assert first is second

    def test_clear_invalidates_instance_cache(self):
        """After clear and re-register, get_exporter returns a new instance."""
        TestExporter = _make_test_exporter("reinst_fmt")
        ExporterRegistry.register(TestExporter)
        first = ExporterRegistry.get_exporter("reinst_fmt")

        ExporterRegistry.clear()
        ExporterRegistry.register(TestExporter)
        second = ExporterRegistry.get_exporter("reinst_fmt")

        assert first is not second

    def test_instance_cache_is_per_format(self):
        """Different formats have different cached instances."""
        ExporterA = _make_test_exporter("fmt_a", ".a")
        ExporterB = _make_test_exporter("fmt_b", ".b")
        ExporterRegistry.register(ExporterA)
        ExporterRegistry.register(ExporterB)

        a = ExporterRegistry.get_exporter("fmt_a")
        b = ExporterRegistry.get_exporter("fmt_b")

        assert a is not b
        assert a.format_name == "fmt_a"
        assert b.format_name == "fmt_b"


class TestExporterRegistryEdgeCases:
    """Edge cases for exporter registration."""

    def setup_method(self):
        self._orig_exporters = dict(ExporterRegistry._exporters)
        self._orig_instances = dict(ExporterRegistry._instances)

    def teardown_method(self):
        ExporterRegistry._exporters.clear()
        ExporterRegistry._exporters.update(self._orig_exporters)
        ExporterRegistry._instances.clear()
        ExporterRegistry._instances.update(self._orig_instances)

    def test_register_overwrites_same_format(self):
        """Registering two exporters with same format_name: last wins."""
        ExporterA = _make_test_exporter("dup_fmt", ".a", "type/a")
        ExporterB = _make_test_exporter("dup_fmt", ".b", "type/b")

        ExporterRegistry.register(ExporterA)
        ExporterRegistry.register(ExporterB)

        # Class dict should have ExporterB
        assert ExporterRegistry._exporters["dup_fmt"] is ExporterB
        # Instance from old class should not be cached
        exporter = ExporterRegistry.get_exporter("dup_fmt")
        assert exporter.file_extension == ".b"

    def test_register_normalizes_format_name_to_lower(self):
        """Format names are lowercased during registration."""
        ExporterUpper = _make_test_exporter("UPPER_FMT")
        ExporterRegistry.register(ExporterUpper)

        assert "upper_fmt" in ExporterRegistry._exporters
        assert ExporterRegistry.is_format_supported("upper_fmt")
        assert ExporterRegistry.is_format_supported("UPPER_FMT")

    def test_get_exporter_normalizes_case(self):
        """get_exporter lowercases the requested format name."""
        TestExporter = _make_test_exporter("mixed_case")
        ExporterRegistry.register(TestExporter)

        assert ExporterRegistry.get_exporter("MIXED_CASE") is not None
        assert ExporterRegistry.get_exporter("Mixed_Case") is not None

    def test_register_returns_class_unchanged(self):
        """register() returns the class unmodified (decorator pattern)."""
        TestExporter = _make_test_exporter("decorator_fmt")

        result = ExporterRegistry.register(TestExporter)

        assert result is TestExporter

    def test_get_available_formats_returns_list(self):
        """get_available_formats returns a list, not dict_keys."""
        formats = ExporterRegistry.get_available_formats()
        assert isinstance(formats, list)

    def test_export_through_registry(self):
        """End-to-end: register, get, and export through registry."""
        TestExporter = _make_test_exporter("e2e_fmt")
        ExporterRegistry.register(TestExporter)

        exporter = ExporterRegistry.get_exporter("e2e_fmt")
        result = exporter.export("# Hello World")

        assert result.content == b"# Hello World"
        assert result.filename == "test.test"
        assert result.mimetype == "application/x-test"

    def test_overwrite_invalidates_old_cached_instance(self):
        """Overwriting registration but old instance is still cached."""
        ExporterA = _make_test_exporter("overwrite_fmt", ".a")
        ExporterRegistry.register(ExporterA)
        old_instance = ExporterRegistry.get_exporter("overwrite_fmt")
        assert old_instance.file_extension == ".a"

        # Re-register with different class
        ExporterB = _make_test_exporter("overwrite_fmt", ".b")
        ExporterRegistry.register(ExporterB)

        # Old cached instance is still returned (known behavior)
        cached = ExporterRegistry.get_exporter("overwrite_fmt")
        # The cache retains the old instance until clear()
        assert cached is old_instance

    def test_overwrite_after_clearing_instance_cache(self):
        """After manually clearing _instances, new class is used."""
        ExporterA = _make_test_exporter("fresh_fmt", ".a")
        ExporterRegistry.register(ExporterA)
        ExporterRegistry.get_exporter("fresh_fmt")  # populate cache

        ExporterB = _make_test_exporter("fresh_fmt", ".b")
        ExporterRegistry.register(ExporterB)
        # Clear only instances, not exporters
        ExporterRegistry._instances.pop("fresh_fmt", None)

        exporter = ExporterRegistry.get_exporter("fresh_fmt")
        assert exporter.file_extension == ".b"
