"""Tests for the Warsaw districts Anki generator."""

from __future__ import annotations

from pathlib import Path
from unittest.mock import patch

import matplotlib.pyplot as plt
import pytest

try:
    from python_pkg.anki_decks.warsaw_districts.warsaw_districts_anki import (
        WARSAW_DISTRICTS,
        create_district_map,
        generate_anki_package,
        generate_district_image_bytes,
        load_district_data,
        main,
    )
except ImportError:
    import sys

    sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent.parent))
    from python_pkg.anki_decks.warsaw_districts.warsaw_districts_anki import (
        WARSAW_DISTRICTS,
        create_district_map,
        generate_anki_package,
        generate_district_image_bytes,
        load_district_data,
        main,
    )


class TestDistricts:
    """Tests for Warsaw districts data."""

    def test_has_18_districts(self) -> None:
        """Test that we have exactly 18 Warsaw districts."""
        assert len(WARSAW_DISTRICTS) == 18

    def test_all_districts_are_strings(self) -> None:
        """Test that all district entries are strings."""
        for district in WARSAW_DISTRICTS:
            assert isinstance(district, str)
            assert len(district) > 0

    def test_districts_are_unique(self) -> None:
        """Test that all district names are unique."""
        assert len(WARSAW_DISTRICTS) == len(set(WARSAW_DISTRICTS))

    def test_known_districts_present(self) -> None:
        """Test that all known Warsaw districts are in the list."""
        district_set = set(WARSAW_DISTRICTS)
        # Check all 18 districts
        expected_districts = {
            "Bemowo",
            "Białołęka",
            "Bielany",
            "Mokotów",
            "Ochota",
            "Praga Południe",  # Note: space, not hyphen
            "Praga Północ",  # Note: space, not hyphen
            "Rembertów",
            "Śródmieście",
            "Targówek",
            "Ursus",
            "Ursynów",
            "Wawer",
            "Wesoła",
            "Wilanów",
            "Włochy",
            "Wola",
            "Żoliborz",
        }
        assert district_set == expected_districts


class TestCreateDistrictMap:
    """Tests for creating district maps."""

    def test_creates_figure(self) -> None:
        """Test that create_district_map returns a Figure."""
        district = WARSAW_DISTRICTS[0]
        fig = create_district_map(district)
        assert fig is not None
        # Clean up
        plt.close(fig)

    def test_creates_figure_for_all_districts(self) -> None:
        """Test that we can create maps for all districts."""
        for district in WARSAW_DISTRICTS:
            fig = create_district_map(district)
            assert fig is not None
            plt.close(fig)


class TestGenerateDistrictImageBytes:
    """Tests for generating district image bytes."""

    def test_generates_bytes(self) -> None:
        """Test that generate_district_image_bytes returns bytes."""
        district = WARSAW_DISTRICTS[0]
        image_bytes = generate_district_image_bytes(district)
        assert isinstance(image_bytes, bytes)
        assert len(image_bytes) > 0

    def test_generates_for_all_districts(self) -> None:
        """Test that we can generate images for all districts."""
        for district in WARSAW_DISTRICTS:
            image_bytes = generate_district_image_bytes(district)
            assert isinstance(image_bytes, bytes)
            assert len(image_bytes) > 0


class TestGenerateAnkiPackage:
    """Tests for generating Anki package."""

    def test_generates_package(self) -> None:
        """Test that output is a genanki Package."""
        package = generate_anki_package("Test Deck")
        assert package is not None
        assert len(package.decks) == 1

    def test_generates_notes_for_all_districts(self) -> None:
        """Test that package contains cards for all 18 districts."""
        package = generate_anki_package()
        deck = package.decks[0]
        assert len(deck.notes) == len(WARSAW_DISTRICTS)

    def test_custom_deck_name(self) -> None:
        """Test that custom deck name is used."""
        package = generate_anki_package("Custom Deck")
        deck = package.decks[0]
        assert deck.name == "Custom Deck"


class TestMain:
    """Tests for the main CLI function."""

    def test_creates_output_file(self, tmp_path: Path) -> None:
        """Test that main creates the output file."""
        output_file = tmp_path / "test_output.apkg"

        result = main(
            [
                "--output",
                str(output_file),
            ]
        )

        assert result == 0
        assert output_file.exists()

    def test_custom_deck_name(self, tmp_path: Path) -> None:
        """Test that custom deck name is used."""
        output_file = tmp_path / "test_output.apkg"

        result = main(
            [
                "--output",
                str(output_file),
                "--deck-name",
                "Custom Deck",
            ]
        )

        assert result == 0
        assert output_file.exists()

    def test_help_flag(self) -> None:
        """Test that --help works."""
        with pytest.raises(SystemExit) as exc_info:
            main(["--help"])
        assert exc_info.value.code == 0

    def test_main_error_returns_1(self, tmp_path: Path) -> None:
        """Test that main returns 1 on error."""
        with patch(
            "python_pkg.anki_decks.warsaw_districts.warsaw_districts_anki"
            ".generate_anki_package",
            side_effect=OSError("disk full"),
        ):
            result = main(["--output", str(tmp_path / "out.apkg")])
        assert result == 1


class TestLoadDistrictData:
    """Tests for load_district_data."""

    def test_missing_geojson_raises_file_not_found(self, tmp_path: Path) -> None:
        """Test FileNotFoundError when GeoJSON file is missing."""
        with (
            patch(
                "python_pkg.anki_decks.warsaw_districts.warsaw_districts_anki"
                ".GEOJSON_PATH",
                tmp_path / "nonexistent.geojson",
            ),
            pytest.raises(FileNotFoundError, match="GeoJSON file not found"),
        ):
            load_district_data()


class TestCreateDistrictMapErrors:
    """Tests for create_district_map error paths."""

    def test_unknown_district_raises_value_error(self) -> None:
        """Test ValueError when district name is not found."""
        with pytest.raises(ValueError, match="not found in data"):
            create_district_map("NonexistentDistrict123")


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