"""
Tests for AOI Packager Service
"""
import pytest
from unittest.mock import patch, MagicMock, AsyncMock
import json

import sys
sys.path.insert(0, '/app')

from services.aoi_packager import AOIPackagerService


class TestAOIPackagerService:
    """Tests for AOI Packager Service."""

    @pytest.fixture
    def service(self):
        """Create service instance."""
        return AOIPackagerService()

    def test_calculate_area_km2_small_polygon(self, service):
        """Test area calculation for small polygon."""
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [9.19, 47.70],
                    [9.20, 47.70],
                    [9.20, 47.71],
                    [9.19, 47.71],
                    [9.19, 47.70],
                ]
            ],
        }
        area = service.calculate_area_km2(polygon)
        # Should be approximately 1 km² (rough estimate)
        assert 0.5 < area < 2.0

    def test_calculate_area_km2_mainau(self, service):
        """Test area calculation for Mainau island polygon."""
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [9.1875, 47.7055],
                    [9.1975, 47.7055],
                    [9.1975, 47.7115],
                    [9.1875, 47.7115],
                    [9.1875, 47.7055],
                ]
            ],
        }
        area = service.calculate_area_km2(polygon)
        # Mainau template claims ~0.45 km²
        assert 0.3 < area < 1.0

    def test_is_within_germany_valid(self, service):
        """Test polygon within Germany."""
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [9.19, 47.70],
                    [9.20, 47.70],
                    [9.20, 47.71],
                    [9.19, 47.71],
                    [9.19, 47.70],
                ]
            ],
        }
        assert service.is_within_germany(polygon) == True

    def test_is_within_germany_outside(self, service):
        """Test polygon outside Germany."""
        # Paris
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [2.3, 48.8],
                    [2.4, 48.8],
                    [2.4, 48.9],
                    [2.3, 48.9],
                    [2.3, 48.8],
                ]
            ],
        }
        assert service.is_within_germany(polygon) == False

    def test_validate_polygon_valid(self, service):
        """Test valid polygon validation."""
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [9.19, 47.70],
                    [9.20, 47.70],
                    [9.20, 47.71],
                    [9.19, 47.71],
                    [9.19, 47.70],
                ]
            ],
        }
        is_valid, message = service.validate_polygon(polygon)
        assert is_valid == True
        assert message == "Valid"

    def test_validate_polygon_not_closed(self, service):
        """Test polygon validation with unclosed ring."""
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [9.19, 47.70],
                    [9.20, 47.70],
                    [9.20, 47.71],
                    [9.19, 47.71],
                    # Missing closing point
                ]
            ],
        }
        is_valid, message = service.validate_polygon(polygon)
        assert is_valid == False
        assert "closed" in message.lower()

    def test_validate_polygon_wrong_type(self, service):
        """Test polygon validation with wrong geometry type."""
        polygon = {
            "type": "Point",
            "coordinates": [9.19, 47.70],
        }
        is_valid, message = service.validate_polygon(polygon)
        assert is_valid == False
        assert "Polygon" in message

    def test_estimate_bundle_size_low(self, service):
        """Test bundle size estimation for low quality."""
        size = service.estimate_bundle_size_mb(1.0, "low")
        assert size == 10.0

    def test_estimate_bundle_size_medium(self, service):
        """Test bundle size estimation for medium quality."""
        size = service.estimate_bundle_size_mb(1.0, "medium")
        assert size == 25.0

    def test_estimate_bundle_size_high(self, service):
        """Test bundle size estimation for high quality."""
        size = service.estimate_bundle_size_mb(1.0, "high")
        assert size == 50.0

    def test_estimate_bundle_size_scales_with_area(self, service):
        """Test bundle size scales with area."""
        size_1km = service.estimate_bundle_size_mb(1.0, "medium")
        size_2km = service.estimate_bundle_size_mb(2.0, "medium")
        assert size_2km == size_1km * 2


class TestGeoUtils:
    """Tests for geo utility functions."""

    def test_lat_lon_to_tile_berlin(self):
        """Test tile conversion for Berlin."""
        from utils.geo_utils import lat_lon_to_tile

        # Berlin: 52.52, 13.405
        x, y = lat_lon_to_tile(52.52, 13.405, 10)
        # At zoom 10, Berlin should be around tile 550, 335
        assert 540 < x < 560
        assert 325 < y < 345

    def test_tile_to_bounds(self):
        """Test tile to bounds conversion."""
        from utils.geo_utils import tile_to_bounds

        west, south, east, north = tile_to_bounds(10, 550, 335)
        # Should return valid bounds
        assert west < east
        assert south < north
        # Should be somewhere in Germany
        assert 5 < west < 20
        assert 45 < south < 60

    def test_calculate_distance(self):
        """Test distance calculation."""
        from utils.geo_utils import calculate_distance

        # Berlin to Munich: ~504 km
        dist = calculate_distance(52.52, 13.405, 48.1351, 11.582)
        assert 450000 < dist < 550000  # meters

    def test_get_germany_bounds(self):
        """Test Germany bounds."""
        from utils.geo_utils import get_germany_bounds

        west, south, east, north = get_germany_bounds()
        assert west == 5.87
        assert south == 47.27
        assert east == 15.04
        assert north == 55.06


class TestLicenseChecker:
    """Tests for license checker utility."""

    def test_osm_source_allowed(self):
        """Test OSM source is allowed."""
        from utils.license_checker import LicenseChecker, DataSource

        assert LicenseChecker.is_source_allowed(DataSource.OPENSTREETMAP) == True

    def test_copernicus_source_allowed(self):
        """Test Copernicus source is allowed."""
        from utils.license_checker import LicenseChecker, DataSource

        assert LicenseChecker.is_source_allowed(DataSource.COPERNICUS_DEM) == True

    def test_google_source_forbidden(self):
        """Test Google source is forbidden."""
        from utils.license_checker import LicenseChecker, DataSource

        assert LicenseChecker.is_source_allowed(DataSource.GOOGLE) == False

    def test_validate_osm_url(self):
        """Test OSM URL validation."""
        from utils.license_checker import LicenseChecker

        is_allowed, message = LicenseChecker.validate_url(
            "https://tile.openstreetmap.org/10/550/335.png"
        )
        assert is_allowed == True
        assert "ALLOWED" in message

    def test_validate_google_url(self):
        """Test Google URL validation."""
        from utils.license_checker import LicenseChecker

        is_allowed, message = LicenseChecker.validate_url(
            "https://maps.googleapis.com/maps/api/staticmap"
        )
        assert is_allowed == False
        assert "FORBIDDEN" in message

    def test_get_attribution_for_sources(self):
        """Test getting attribution for sources."""
        from utils.license_checker import LicenseChecker, DataSource

        sources = [DataSource.OPENSTREETMAP, DataSource.COPERNICUS_DEM]
        attributions = LicenseChecker.get_attribution_for_sources(sources)

        assert len(attributions) == 2
        assert any("OpenStreetMap" in a["name"] for a in attributions)
        assert any("Copernicus" in a["name"] for a in attributions)

    def test_check_commercial_use_allowed(self):
        """Test commercial use check for allowed sources."""
        from utils.license_checker import LicenseChecker, DataSource

        sources = [DataSource.OPENSTREETMAP, DataSource.COPERNICUS_DEM]
        allowed, issues = LicenseChecker.check_commercial_use(sources)
        assert allowed == True
        assert len(issues) == 0

    def test_check_commercial_use_forbidden(self):
        """Test commercial use check with forbidden source."""
        from utils.license_checker import LicenseChecker, DataSource

        sources = [DataSource.OPENSTREETMAP, DataSource.GOOGLE]
        allowed, issues = LicenseChecker.check_commercial_use(sources)
        assert allowed == False
        assert len(issues) > 0
