"""
OSM Extractor Service
Extracts OpenStreetMap features from PostGIS or vector tiles
"""
import os
import json
from typing import Optional
import structlog
from shapely.geometry import shape, mapping

from config import settings

logger = structlog.get_logger(__name__)


class OSMExtractorService:
    """
    Service for extracting OSM features from a geographic area.

    Can extract from:
    - PostGIS database (imported OSM data)
    - PMTiles archive
    """

    def __init__(self):
        self.database_url = settings.database_url

    async def extract_features(self, polygon: dict) -> dict:
        """
        Extract OSM features within a polygon.

        Returns a GeoJSON FeatureCollection with categorized features.
        """
        geom = shape(polygon)
        bounds = geom.bounds  # (minx, miny, maxx, maxy)

        # Feature collection structure
        features = {
            "type": "FeatureCollection",
            "features": [],
            "metadata": {
                "source": "OpenStreetMap",
                "license": "ODbL",
                "bounds": {
                    "west": bounds[0],
                    "south": bounds[1],
                    "east": bounds[2],
                    "north": bounds[3],
                },
            },
        }

        # Try to extract from database
        try:
            db_features = await self._extract_from_database(geom)
            if db_features:
                features["features"].extend(db_features)
                return features
        except Exception as e:
            logger.warning("Database extraction failed", error=str(e))

        # Fall back to mock data for development
        features["features"] = self._generate_mock_features(geom)
        features["metadata"]["source"] = "Mock Data (OSM data not imported)"

        return features

    async def _extract_from_database(self, geom) -> list:
        """Extract features from PostGIS database."""
        # This would use asyncpg to query the database
        # For now, return empty list to trigger mock data

        # Example query structure (not executed):
        # SELECT ST_AsGeoJSON(way), name, building, highway, natural, waterway
        # FROM planet_osm_polygon
        # WHERE ST_Intersects(way, ST_GeomFromGeoJSON($1))

        return []

    def _generate_mock_features(self, geom) -> list:
        """Generate mock OSM features for development."""
        from shapely.geometry import Point, LineString, Polygon as ShapelyPolygon
        import random

        bounds = geom.bounds
        features = []

        # Generate some mock buildings
        for i in range(5):
            x = random.uniform(bounds[0], bounds[2])
            y = random.uniform(bounds[1], bounds[3])

            # Small building polygon
            size = 0.0002  # ~20m
            building = ShapelyPolygon([
                (x, y),
                (x + size, y),
                (x + size, y + size),
                (x, y + size),
                (x, y),
            ])

            if geom.contains(building.centroid):
                features.append({
                    "type": "Feature",
                    "geometry": mapping(building),
                    "properties": {
                        "category": "building",
                        "building": "yes",
                        "name": f"Gebäude {i + 1}",
                    },
                })

        # Generate some mock roads
        for i in range(3):
            x1 = random.uniform(bounds[0], bounds[2])
            y1 = random.uniform(bounds[1], bounds[3])
            x2 = random.uniform(bounds[0], bounds[2])
            y2 = random.uniform(bounds[1], bounds[3])

            road = LineString([(x1, y1), (x2, y2)])

            features.append({
                "type": "Feature",
                "geometry": mapping(road),
                "properties": {
                    "category": "road",
                    "highway": random.choice(["primary", "secondary", "residential"]),
                    "name": f"Straße {i + 1}",
                },
            })

        # Generate mock water feature
        cx = (bounds[0] + bounds[2]) / 2
        cy = (bounds[1] + bounds[3]) / 2
        size = min(bounds[2] - bounds[0], bounds[3] - bounds[1]) * 0.2

        water = ShapelyPolygon([
            (cx - size, cy - size / 2),
            (cx + size, cy - size / 2),
            (cx + size, cy + size / 2),
            (cx - size, cy + size / 2),
            (cx - size, cy - size / 2),
        ])

        if geom.intersects(water):
            features.append({
                "type": "Feature",
                "geometry": mapping(water.intersection(geom)),
                "properties": {
                    "category": "water",
                    "natural": "water",
                    "name": "See",
                },
            })

        # Generate mock forest
        forest_size = size * 1.5
        forest = ShapelyPolygon([
            (bounds[0], bounds[1]),
            (bounds[0] + forest_size, bounds[1]),
            (bounds[0] + forest_size, bounds[1] + forest_size),
            (bounds[0], bounds[1] + forest_size),
            (bounds[0], bounds[1]),
        ])

        if geom.intersects(forest):
            features.append({
                "type": "Feature",
                "geometry": mapping(forest.intersection(geom)),
                "properties": {
                    "category": "vegetation",
                    "landuse": "forest",
                    "name": "Wald",
                },
            })

        return features

    async def get_feature_statistics(self, polygon: dict) -> dict:
        """Get statistics about features in an area."""
        features = await self.extract_features(polygon)

        categories = {}
        for feature in features.get("features", []):
            category = feature.get("properties", {}).get("category", "other")
            categories[category] = categories.get(category, 0) + 1

        return {
            "total_features": len(features.get("features", [])),
            "by_category": categories,
        }

    async def search_features(
        self,
        polygon: dict,
        category: str,
        name_filter: Optional[str] = None,
    ) -> list:
        """Search for specific features within an area."""
        all_features = await self.extract_features(polygon)

        filtered = []
        for feature in all_features.get("features", []):
            props = feature.get("properties", {})

            if props.get("category") != category:
                continue

            if name_filter:
                name = props.get("name", "")
                if name_filter.lower() not in name.lower():
                    continue

            filtered.append(feature)

        return filtered
