"""
Tests for Tile Server API
"""
import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch, MagicMock

# Import app after mocking to avoid import errors
import sys
sys.path.insert(0, '/app')


@pytest.fixture
def client():
    """Create test client."""
    from main import app
    return TestClient(app)


class TestTileEndpoints:
    """Tests for tile server endpoints."""

    def test_health_check(self, client):
        """Test health check endpoint returns healthy status."""
        response = client.get("/health")
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "healthy"
        assert data["service"] == "geo-service"
        assert "data_status" in data

    def test_root_endpoint(self, client):
        """Test root endpoint returns service info."""
        response = client.get("/")
        assert response.status_code == 200
        data = response.json()
        assert data["service"] == "GeoEdu Service"
        assert "endpoints" in data
        assert "attribution" in data

    def test_tile_metadata(self, client):
        """Test tile metadata endpoint."""
        response = client.get("/api/v1/tiles/metadata")
        assert response.status_code == 200
        data = response.json()
        assert "name" in data
        assert "minzoom" in data
        assert "maxzoom" in data
        assert "bounds" in data
        assert data["attribution"] == "© OpenStreetMap contributors (ODbL)"

    def test_tile_bounds(self, client):
        """Test tile bounds endpoint returns Germany bounds."""
        response = client.get("/api/v1/tiles/bounds")
        assert response.status_code == 200
        data = response.json()
        assert "bounds" in data
        # Germany approximate bounds
        bounds = data["bounds"]
        assert bounds[0] < 6  # west
        assert bounds[1] > 47  # south
        assert bounds[2] > 14  # east
        assert bounds[3] < 56  # north

    def test_style_json(self, client):
        """Test MapLibre style endpoint."""
        response = client.get("/api/v1/tiles/style.json")
        assert response.status_code == 200
        data = response.json()
        assert data["version"] == 8
        assert "sources" in data
        assert "layers" in data
        assert "osm" in data["sources"]

    def test_tile_request_without_data(self, client):
        """Test tile request returns 503 when data not available."""
        response = client.get("/api/v1/tiles/0/0/0.pbf")
        # Should return 503 (data not available) or 204 (empty tile)
        assert response.status_code in [204, 503]

    def test_tile_request_invalid_zoom(self, client):
        """Test tile request with invalid zoom level."""
        response = client.get("/api/v1/tiles/25/0/0.pbf")
        assert response.status_code == 422  # Validation error


class TestTerrainEndpoints:
    """Tests for terrain/DEM endpoints."""

    def test_terrain_metadata(self, client):
        """Test terrain metadata endpoint."""
        response = client.get("/api/v1/terrain/metadata")
        assert response.status_code == 200
        data = response.json()
        assert data["name"] == "Copernicus DEM GLO-30"
        assert data["resolution_m"] == 30
        assert "bounds" in data

    def test_heightmap_request_without_data(self, client):
        """Test heightmap request returns appropriate status when no data."""
        response = client.get("/api/v1/terrain/10/500/500.png")
        # Should return 204 (no content) or 503 (data not available)
        assert response.status_code in [204, 503]

    def test_elevation_at_point_within_germany(self, client):
        """Test elevation endpoint with valid Germany coordinates."""
        # Berlin coordinates
        response = client.get("/api/v1/terrain/elevation?lat=52.52&lon=13.405")
        # Should return 404 (no data) or 200 (with elevation) or 503
        assert response.status_code in [200, 404, 503]

    def test_elevation_at_point_outside_germany(self, client):
        """Test elevation endpoint with coordinates outside Germany."""
        # Paris coordinates (outside Germany)
        response = client.get("/api/v1/terrain/elevation?lat=48.8566&lon=2.3522")
        # Should return 422 (validation error - outside bounds)
        assert response.status_code == 422


class TestAOIEndpoints:
    """Tests for AOI (Area of Interest) endpoints."""

    def test_validate_polygon_valid(self, client):
        """Test polygon validation with valid polygon."""
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [9.19, 47.71],
                    [9.20, 47.71],
                    [9.20, 47.70],
                    [9.19, 47.70],
                    [9.19, 47.71],
                ]
            ],
        }
        response = client.post("/api/v1/aoi/validate", json=polygon)
        assert response.status_code == 200
        data = response.json()
        assert "valid" in data
        assert "area_km2" in data
        assert "within_germany" in data

    def test_validate_polygon_too_large(self, client):
        """Test polygon validation with too large area."""
        # Large polygon (> 4 km²)
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [9.0, 47.5],
                    [9.5, 47.5],
                    [9.5, 48.0],
                    [9.0, 48.0],
                    [9.0, 47.5],
                ]
            ],
        }
        response = client.post("/api/v1/aoi/validate", json=polygon)
        assert response.status_code == 200
        data = response.json()
        assert data["within_size_limit"] == False

    def test_validate_polygon_outside_germany(self, client):
        """Test polygon validation outside Germany."""
        # Paris area
        polygon = {
            "type": "Polygon",
            "coordinates": [
                [
                    [2.3, 48.8],
                    [2.4, 48.8],
                    [2.4, 48.9],
                    [2.3, 48.9],
                    [2.3, 48.8],
                ]
            ],
        }
        response = client.post("/api/v1/aoi/validate", json=polygon)
        assert response.status_code == 200
        data = response.json()
        assert data["within_germany"] == False

    def test_mainau_template(self, client):
        """Test Mainau demo template endpoint."""
        response = client.get("/api/v1/aoi/templates/mainau")
        assert response.status_code == 200
        data = response.json()
        assert data["name"] == "Insel Mainau"
        assert "polygon" in data
        assert "center" in data
        assert "suggested_themes" in data

    def test_create_aoi(self, client):
        """Test AOI creation with valid polygon."""
        request = {
            "polygon": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [9.1875, 47.7055],
                        [9.1975, 47.7055],
                        [9.1975, 47.7115],
                        [9.1875, 47.7115],
                        [9.1875, 47.7055],
                    ]
                ],
            },
            "theme": "topographie",
            "quality": "medium",
        }
        response = client.post("/api/v1/aoi", json=request)
        assert response.status_code == 200
        data = response.json()
        assert "aoi_id" in data
        assert data["status"] == "queued"
        assert "area_km2" in data

    def test_create_aoi_too_large(self, client):
        """Test AOI creation fails with too large area."""
        request = {
            "polygon": {
                "type": "Polygon",
                "coordinates": [
                    [
                        [9.0, 47.5],
                        [9.5, 47.5],
                        [9.5, 48.0],
                        [9.0, 48.0],
                        [9.0, 47.5],
                    ]
                ],
            },
            "theme": "topographie",
            "quality": "medium",
        }
        response = client.post("/api/v1/aoi", json=request)
        assert response.status_code == 400

    def test_get_nonexistent_aoi(self, client):
        """Test getting non-existent AOI returns 404."""
        response = client.get("/api/v1/aoi/nonexistent-id")
        assert response.status_code == 404


class TestLearningEndpoints:
    """Tests for Learning Nodes endpoints."""

    def test_learning_templates(self, client):
        """Test learning templates endpoint."""
        response = client.get("/api/v1/learning/templates")
        assert response.status_code == 200
        data = response.json()
        assert "themes" in data
        assert "difficulties" in data
        assert len(data["themes"]) == 6  # 6 themes

        # Check theme structure
        theme = data["themes"][0]
        assert "id" in theme
        assert "name" in theme
        assert "description" in theme
        assert "example_questions" in theme

    def test_learning_statistics(self, client):
        """Test learning statistics endpoint."""
        response = client.get("/api/v1/learning/statistics")
        assert response.status_code == 200
        data = response.json()
        assert "total_nodes_generated" in data
        assert "nodes_by_theme" in data

    def test_generate_nodes_without_aoi(self, client):
        """Test node generation fails without valid AOI."""
        request = {
            "aoi_id": "nonexistent-aoi",
            "theme": "topographie",
            "difficulty": "mittel",
            "node_count": 5,
        }
        response = client.post("/api/v1/learning/generate", json=request)
        # Should return 404 (AOI not found) or 503 (Ollama not available)
        assert response.status_code in [404, 503]
