"""Tests for OpenStreetMap utilities (osm_utils.py)."""

from pathlib import Path
from unittest.mock import MagicMock, patch, mock_open

import pytest

from walkingpandas.io.match.osm_utils import (
    download_region,
    generate_cache_key,
    geocode_place_to_bbox,
)


class TestGeocodePlaceToBbox:
    @patch("walkingpandas.io.match.osm_utils.requests.get")
    def test_success_with_bounding_box(self, mock_get):
        mock_response = MagicMock()
        mock_response.json.return_value = [
            {"boundingbox": ["47.32", "47.43", "8.45", "8.63"]}
        ]
        mock_response.raise_for_status = MagicMock()
        mock_get.return_value = mock_response

        bbox = geocode_place_to_bbox("Zurich")

        assert bbox == pytest.approx((47.43, 47.32, 8.63, 8.45))
        mock_get.assert_called_once()

    @patch("walkingpandas.io.match.osm_utils.requests.get")
    def test_no_bounding_box_uses_fallback(self, mock_get):
        mock_response = MagicMock()
        mock_response.json.return_value = [
            {"lat": "47.38", "lon": "8.54", "boundingbox": None}
        ]
        mock_response.raise_for_status = MagicMock()
        mock_get.return_value = mock_response

        bbox = geocode_place_to_bbox("Small Place")

        north, south, east, west = bbox
        assert north == pytest.approx(47.39)
        assert south == pytest.approx(47.37)

    @patch("walkingpandas.io.match.osm_utils.requests.get")
    def test_place_not_found_raises(self, mock_get):
        mock_response = MagicMock()
        mock_response.json.return_value = []
        mock_response.raise_for_status = MagicMock()
        mock_get.return_value = mock_response

        with pytest.raises(ValueError, match="Place not found"):
            geocode_place_to_bbox("Nonexistent Place XYZ")


class TestGenerateCacheKeyOsmPbf:
    def test_existing_file(self, tmp_path):
        pbf = tmp_path / "test.osm.pbf"
        pbf.write_bytes(b"fake pbf data")
        key = generate_cache_key(osm_pbf=pbf)
        assert isinstance(key, str) and len(key) == 16

    def test_missing_file(self, tmp_path):
        pbf = tmp_path / "missing.osm.pbf"
        key = generate_cache_key(osm_pbf=pbf)
        assert isinstance(key, str) and len(key) == 16

    def test_existing_vs_missing_differ(self, tmp_path):
        pbf = tmp_path / "test.osm.pbf"
        key_missing = generate_cache_key(osm_pbf=pbf)
        pbf.write_bytes(b"data")
        key_exists = generate_cache_key(osm_pbf=pbf)
        assert key_missing != key_exists


class TestDownloadRegion:
    @patch("walkingpandas.io.match.osm_utils.requests.post")
    def test_happy_path(self, mock_post, tmp_path):
        mock_response = MagicMock()
        mock_response.content = b"<osm>fake xml</osm>"
        mock_response.raise_for_status = MagicMock()
        mock_post.return_value = mock_response

        xml_out = str(tmp_path / "map.osm")
        pbf_out = str(tmp_path / "map.osm.pbf")

        mock_writer = MagicMock()
        mock_writer.__enter__ = MagicMock(return_value=mock_writer)
        mock_writer.__exit__ = MagicMock(return_value=False)

        mock_processor = MagicMock()
        mock_processor.__iter__ = MagicMock(return_value=iter([]))

        with patch.dict("sys.modules", {"osmium": MagicMock()}) as _:
            import sys
            mock_osmium = sys.modules["osmium"]
            mock_osmium.SimpleWriter.return_value = mock_writer
            mock_osmium.FileProcessor.return_value = mock_processor

            xml_result, pbf_result = download_region(
                bbox=(47.4, 47.3, 8.6, 8.5),
                output_xml=xml_out,
                output_pbf=pbf_out,
            )

        assert Path(xml_result).exists()
        mock_post.assert_called_once()

    @patch("walkingpandas.io.match.osm_utils.requests.post")
    def test_osmium_import_error(self, mock_post, tmp_path):
        mock_response = MagicMock()
        mock_response.content = b"<osm>data</osm>"
        mock_response.raise_for_status = MagicMock()
        mock_post.return_value = mock_response

        xml_out = str(tmp_path / "map.osm")
        pbf_out = str(tmp_path / "map.osm.pbf")

        with patch.dict("sys.modules", {"osmium": None}):
            with pytest.raises(ImportError, match="osmium"):
                download_region(
                    bbox=(47.4, 47.3, 8.6, 8.5),
                    output_xml=xml_out,
                    output_pbf=pbf_out,
                )
