from fastapi import APIRouter, HTTPException, Body, Request
import httpx
import json
from typing import List, Optional
import math
import random
from time import time
from geopy.geocoders import Nominatim
from geopy.distance import geodesic

router = APIRouter(prefix="/api/location", tags=["location"])

# Service distance limit in kilometers
SERVICE_DISTANCE_LIMIT = 534

# Simple rate limiter
request_counts = {}
RATE_LIMIT = 30  # requests per minute
RATE_WINDOW = 60  # seconds

def check_rate_limit(client_ip: str):
    current_time = time()
    
    # Clean old entries
    if client_ip in request_counts:
        request_counts[client_ip] = [
            req_time for req_time in request_counts[client_ip]
            if current_time - req_time < RATE_WINDOW
        ]
    else:
        request_counts[client_ip] = []
    
    # Check limit
    if len(request_counts[client_ip]) >= RATE_LIMIT:
        raise HTTPException(status_code=429, detail="Rate limit exceeded")
    
    # Add current request
    request_counts[client_ip].append(current_time)

def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
    """
    Calculate distance between two coordinates using Haversine formula
    Returns distance in kilometers
    """
    R = 6371  # Earth's radius in kilometers
    
    lat1_rad = math.radians(lat1)
    lon1_rad = math.radians(lon1)
    lat2_rad = math.radians(lat2)
    lon2_rad = math.radians(lon2)
    
    dlat = lat2_rad - lat1_rad
    dlon = lon2_rad - lon1_rad
    
    a = (math.sin(dlat/2) ** 2 + 
         math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon/2) ** 2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    
    return R * c

@router.post("/validate-distance")
async def validate_service_distance(
    from_lat: float = Body(...),
    from_lon: float = Body(...),
    to_lat: float = Body(...),
    to_lon: float = Body(...)
):
    """
    Validate if the route distance is within service limits (534KM)
    """
    try:
        distance = calculate_distance(from_lat, from_lon, to_lat, to_lon)
        
        is_within_limit = distance <= SERVICE_DISTANCE_LIMIT
        
        # Enhanced response with guidance for exceeded limits
        if is_within_limit:
            response_data = {
                "distance_km": round(distance, 2),
                "is_within_limit": True,
                "service_available": True,
                "limit_km": SERVICE_DISTANCE_LIMIT,
                "message": f"Service available for {round(distance, 2)}KM route",
                "status": "success"
            }
        else:
            response_data = {
                "distance_km": round(distance, 2),
                "is_within_limit": False,
                "service_available": False,
                "limit_km": SERVICE_DISTANCE_LIMIT,
                "message": f"Service not available. Route distance ({round(distance, 2)}KM) exceeds limit ({SERVICE_DISTANCE_LIMIT}KM)",
                "status": "limit_exceeded",
                "guidance": {
                    "title": "Service Limit Exceeded - Here are your options:",
                    "suggestions": [
                        "🔍 Search for nationwide moving companies that handle long-distance moves",
                        "📞 Contact us for special inquiries about extended service areas",
                        "🗺️ Consider choosing a destination within our 534KM service range",
                        "🚚 Look for logistics companies specializing in cross-region transportation",
                        "📧 Email us at info@kitamove.com for custom solutions"
                    ],
                    "contact_info": {
                        "phone": "+60 19-XXX XXXX",
                        "email": "info@kitamove.com",
                        "website": "https://kitamove.com/contact",
                        "hours": "Monday-Friday: 9AM-6PM (MYT)"
                    },
                    "alternative_services": [
                        "Local moving services (within 100KM)",
                        "Regional moving services (100-300KM)",
                        "Inter-city moving services (300-534KM)",
                        "Storage and packing services",
                        "Manpower assistance"
                    ]
                }
            }
        
        return response_data
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Distance calculation error: {str(e)}")

@router.post("/bulk-geocode")
async def bulk_geocode_properties():
    """
    Bulk geocode and update property coordinates
    This is an admin endpoint to fix coordinate accuracy
    """
    try:
        # Coordinate corrections based on manual verification and OpenStreetMap data
        coordinate_corrections = {
            'Bay 21': [5.9925245, 116.0924663],
            'Maya@Likas': [5.9775, 116.0831],
            'Signal Hill Park': [5.9941, 116.0873],
            'The Peak SOHO': [5.9949, 116.0875],
            'Bayshore Condominium': [5.9901, 116.0865],
            'The Peak Vista': [5.9968, 116.0862],
            'Bay Suites': [5.9925, 116.0929],
            'Pelagos Designer Suites@ Water Front': [5.9784, 116.0695],
            'Coral Bay': [5.9732, 116.0579],
            'D\'Banyan Residency @ Sutera': [5.9745, 116.0616],
            'Kristal': [5.9397, 116.0568],
            'Jesselton Twin Towers': [5.9677, 116.0982],
            'Forest Hill': [5.9461, 116.0998],
            'The Gardens': [5.9398, 116.0976],
            'Sky88 Residences': [5.9348, 116.0973],
            'Vetro 11 Designer Suites': [5.9583, 116.0750],
            'K Avenue': [5.9141, 116.0640],
            'Kingfisher Putatan Condominium': [5.8761, 116.0578],
            'V21 Residence': [6.0886, 116.1517],
            'Taman Kingfisher Millennium': [6.0243, 116.1239],
        }
        
        corrections_applied = []
        
        # Note: This would require database access
        # For now, return the corrections that should be applied
        for property_name, coords in coordinate_corrections.items():
            corrections_applied.append({
                'property': property_name,
                'new_coordinates': {
                    'latitude': coords[0],
                    'longitude': coords[1]
                },
                'status': 'ready_for_update'
            })
        
        return {
            'message': f'Found {len(corrections_applied)} properties that need coordinate corrections',
            'corrections': corrections_applied,
            'note': 'Frontend coordinate correction system is active. Database updates would require additional implementation.'
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Distance calculation error: {str(e)}")

@router.get("/geocode")
async def geocode_address(address: str):
    """
    Geocode an address to get accurate coordinates using Nominatim API
    """
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                "https://nominatim.openstreetmap.org/search",
                params={
                    "format": "jsonv2",
                    "countrycodes": "MY",
                    "limit": 1,
                    "dedupe": 1,
                    "q": address + " Malaysia"
                },
                headers={
                    "User-Agent": "KitaMOVE/1.0 (https://kitamove.com; info@kitamove.com)",
                    "Accept": "application/json"
                },
                timeout=10.0
            )
            
            if response.status_code == 200:
                data = response.json()
                if data and len(data) > 0:
                    result = data[0]
                    return {
                        "latitude": float(result["lat"]),
                        "longitude": float(result["lon"]),
                        "display_name": result["display_name"],
                        "accuracy": "high",
                        "source": "openstreetmap"
                    }
                else:
                    raise HTTPException(status_code=404, detail="Address not found")
            else:
                raise HTTPException(status_code=500, detail="Geocoding service unavailable")
                
    except httpx.TimeoutException:
        raise HTTPException(status_code=408, detail="Geocoding request timeout")
    except httpx.RequestError as e:
        raise HTTPException(status_code=500, detail=f"Geocoding error: {str(e)}")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")

@router.get("/search")
async def search_location(q: str, limit: int = 5):
    """
    Search for locations using Nominatim API
    This endpoint proxies requests to avoid CORS issues
    """
    try:
        # Use httpx for async HTTP requests
        async with httpx.AsyncClient() as client:
            response = await client.get(
                "https://nominatim.openstreetmap.org/search",
                params={
                    "format": "jsonv2",
                    "countrycodes": "MY",
                    "limit": limit * 3,  # Get more results to filter for Sabah
                    "dedupe": 1,
                    "q": q
                },
                headers={
                    "User-Agent": "KitaMOVE/1.0 (https://kitamove.com; info@kitamove.com)",
                    "Accept": "application/json"
                },
                timeout=10.0
            )
            
            if response.status_code == 200:
                data = response.json()
                
                # Filter results to focus on Sabah area only
                sabah_locations = []
                for location in data:
                    display_name = location.get('display_name', '').lower()
                    # Check if location is in Sabah
                    if any(keyword in display_name for keyword in ['sabah', 'kota kinabalu', 'sandakan', 'tawau', 'lahad datu', 'keningau', 'ranau', 'kudat', 'semporna', 'beaufort', 'tenom', 'papar', 'penampang', 'tuaran', 'putatan', 'beluran', 'kinabatangan', 'tambunan', 'nabawan', 'kalabakan', 'telupid', 'tongod', 'pitas', 'matunggong', 'kota belud', 'kota marudu', 'pulau banggi']):
                        sabah_locations.append(location)
                        if len(sabah_locations) >= limit:
                            break
                
                # If no Sabah-specific results, return general Malaysian results but prioritize Sabah
                if not sabah_locations:
                    # Return original results but limit to requested amount
                    return data[:limit]
                
                return sabah_locations
            else:
                raise HTTPException(
                    status_code=response.status_code,
                    detail=f"Location search failed: {response.status_code}"
                )
                
    except httpx.TimeoutException:
        raise HTTPException(status_code=408, detail="Location search timeout")
    except httpx.RequestError as e:
        raise HTTPException(status_code=500, detail=f"Location search error: {str(e)}")
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")

@router.get("/pois")
async def get_nearby_pois(request: Request, lat: float, lon: float, radius: int = 1000, limit: int = 20):
    """
    Get Points of Interest near a location with proper fallback
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        print(f"Fetching POIs for location [{lat}, {lon}] with radius {radius}m")
        
        # Validate inputs
        if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
            raise HTTPException(status_code=400, detail="Invalid coordinates")
        
        # Generate fallback POIs
        fallback_pois = generate_fallback_pois(lat, lon, limit)
        
        # ✅ Add debugging to verify coordinates
        for poi in fallback_pois[:3]:  # Check first 3 POIs
            print(f"POI: {poi['name']} at [{poi['lat']}, {poi['lon']}]")
        
        print(f"Returning {len(fallback_pois)} POIs")
        return fallback_pois
        
    except HTTPException:
        raise
    except Exception as e:
        print(f"POI endpoint error: {e}")
        # Return minimal emergency POIs with proper coordinates
        return [
            {
                'id': 'emergency_poi_1',
                'lat': round(lat + 0.001, 7),  # ✅ More precise coordinates
                'lon': round(lon + 0.001, 7),  # ✅ More precise coordinates
                'name': 'Local Service',
                'amenity': 'service',
                'category': 'other',
                'distance': 0.1,
                'tags': {'amenity': 'service', 'name': 'Local Service'}
            }
        ]

@router.get("/real-pois")
async def get_real_pois(request: Request, lat: float, lon: float, radius: int = 15000, limit: int = 100):
    """
    Get comprehensive real POI data using enhanced Overpass API queries
    Optimized for all Sabah property locations
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        print(f"🌍 Fetching comprehensive POIs for location [{lat}, {lon}] with radius {radius}m")
        
        # Validate inputs
        if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
            raise HTTPException(status_code=400, detail="Invalid coordinates")
        
        # Simplified Overpass API query for better reliability
        overpass_query = f"""
        [out:json][timeout:15];
        (
          // Core amenities
          node["amenity"~"^(restaurant|cafe|fast_food|hospital|clinic|pharmacy|school|university|bank|atm|fuel|parking|bus_station)$"](around:{radius},{lat},{lon});
          node["shop"](around:{radius},{lat},{lon});
          node["tourism"](around:{radius},{lat},{lon});
          node["leisure"](around:{radius},{lat},{lon});
        );
        out center;
        """
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://overpass-api.de/api/interpreter",
                data=overpass_query,
                headers={"Content-Type": "text/plain"},
                timeout=30.0
            )
            
            if response.status_code == 200:
                data = response.json()
                
                if data and "elements" in data:
                    print(f"📊 Overpass API returned {len(data['elements'])} elements")
                    
                    pois = []
                    seen_pois = set()  # Track seen POIs to prevent duplicates
                    
                    for element in data["elements"]:
                        if element.get("lat") and element.get("lon"):
                            poi_lat = float(element["lat"])
                            poi_lon = float(element["lon"])
                            
                            # Calculate distance using geopy
                            distance = geodesic((lat, lon), (poi_lat, poi_lon)).kilometers
                            
                            # Skip if too far
                            if distance > (radius / 1000):  # Convert radius to km
                                continue
                            
                            # Extract name and amenity
                            tags = element.get("tags", {})
                            name = tags.get("name") or tags.get("brand") or tags.get("operator") or tags.get("description") or "Local Business"
                            amenity = tags.get("amenity") or tags.get("shop") or tags.get("tourism") or tags.get("leisure") or "other"
                            
                            # Create unique key for deduplication
                            poi_key = f"{name}_{poi_lat}_{poi_lon}"
                            if poi_key in seen_pois:
                                continue
                            seen_pois.add(poi_key)
                            
                            # Categorize POI
                            category = categorize_poi(amenity, name)
                            
                            # Only include POIs in our target categories
                            if category in ['food', 'health', 'education', 'shopping', 'transport', 'banking']:
                                pois.append({
                                    "id": f"real_{element.get('id', 'unknown')}",
                                    "lat": poi_lat,
                                    "lon": poi_lon,
                                    "name": name,
                                    "amenity": amenity,
                                    "category": category,
                                    "distance": round(distance, 2),
                                    "tags": tags,
                                    "source": "real_osm"
                                })
                    
                    # Sort by distance and limit results
                    pois.sort(key=lambda x: x["distance"])
                    pois = pois[:limit]
                    
                    # Log category breakdown
                    category_counts = {}
                    for poi in pois:
                        cat = poi["category"]
                        category_counts[cat] = category_counts.get(cat, 0) + 1
                    
                    print(f"✅ Returning {len(pois)} real POIs from OpenStreetMap")
                    print(f"📊 Category breakdown: {category_counts}")
                    return pois
        
        # No fallback - return empty list if Overpass fails
        print("❌ Overpass API failed - no POI data available")
        return []
        
    except Exception as e:
        print(f"❌ Real POI endpoint error: {e}")
        # Return empty list if any error occurs
        return []


def is_land_coordinate(lat: float, lon: float) -> bool:
    """
    Enhanced land detection for Kota Kinabalu with more precise boundaries
    Returns True if coordinates appear to be on land
    """
    # Kota Kinabalu detailed water body avoidance
    if 5.9 <= lat <= 6.1 and 116.0 <= lon <= 116.2:
        
        # Major water areas to avoid (very precise boundaries)
        
        # 1. Gaya Bay (southwest of KK) - VERY STRICT
        if lat <= 5.975 and lon <= 116.065:
            return False
            
        # 2. Sepanggar Bay (northwest) - STRICT  
        if lat >= 6.02 and lon <= 116.08:
            return False
            
        # 3. South China Sea (northeast and east) - STRICT
        if lat >= 5.99 and lon >= 116.09:
            return False
            
        # 4. Coastal areas (too close to shore) - MODERATE
        if lat <= 5.96 and lon >= 116.05:  # South coastal
            return False
        if lat >= 6.01 and lon >= 116.08:  # North coastal
            return False
            
        # 5. Signal Hill area water features
        if 5.975 <= lat <= 5.985 and 116.075 <= lon <= 116.085:
            # This is mostly land, allow it
            pass
            
        # 6. Sutera Harbour and surrounding water
        if 5.94 <= lat <= 5.97 and 116.04 <= lon <= 116.07:
            return False
            
        # 7. Likas Bay area
        if lat >= 5.995 and 116.07 <= lon <= 116.09:
            return False
            
        # 8. Additional safety: avoid extreme coastal coordinates
        # East coast (too close to sea)
        if lon >= 116.095:
            return False
        # West coast (too close to sea)  
        if lon <= 116.045:
            return False
        # North coast (too close to sea)
        if lat >= 6.015:
            return False
        # South coast (too close to sea)
        if lat <= 5.955:
            return False
    
    # For other Sabah areas, use city proximity check
    sabah_safe_zones = [
        # Major urban areas (guaranteed land)
        (5.9804, 116.0735, 5),   # KK City Centre - 5km radius
        (5.9749, 116.0724, 3),   # KK Downtown - 3km radius  
        (5.9889, 116.0736, 2),   # Likas area - 2km radius
        (5.9650, 116.0850, 2),   # Signal Hill - 2km radius
        (5.9950, 116.0650, 2),   # Sepanggar - 2km radius
        (5.9550, 116.0950, 2),   # Penampang - 2km radius
        
        # Other major Sabah cities
        (5.8402, 118.1179, 10),  # Sandakan
        (4.2514, 117.8861, 10),  # Tawau
        (5.0323, 118.3267, 8),   # Lahad Datu
        (5.3392, 116.1592, 8),   # Keningau
        (4.4788, 118.6103, 5),   # Semporna
        (6.8833, 116.8333, 5),   # Kudat
        (5.3472, 115.7417, 5),   # Beaufort
        (6.3458, 116.4347, 5),   # Kota Belud
        (5.9667, 116.6833, 5),   # Ranau
    ]
    
    # Check if coordinates are within safe zones
    for city_lat, city_lon, radius_km in sabah_safe_zones:
        distance = calculate_distance(lat, lon, city_lat, city_lon)
        if distance <= radius_km:
            return True
    
    # If not in safe zone, be conservative
    return False

def generate_algorithmic_pois(lat: float, lon: float, count: int, start_id: int) -> List[dict]:
    """
    Generate additional POIs with accurate placement
    """
    poi_templates = [
        {"amenity": "restaurant", "category": "food", "names": ["Warung Sabah", "Kedai Makan Seri", "Restoran Nasi Lemak"]},
        {"amenity": "cafe", "category": "food", "names": ["Old Town White Coffee", "Kedai Kopi Towkay", "Cafe Latte"]},
        {"amenity": "convenience", "category": "shopping", "names": ["7-Eleven", "KK Mart", "Speedmart 99"]},
        {"amenity": "clinic", "category": "health", "names": ["Klinik Dr. Lim", "Poliklinik Sabah"]},
        {"amenity": "bank", "category": "banking", "names": ["Maybank", "CIMB Bank", "Public Bank"]},
        {"amenity": "school", "category": "education", "names": ["SK Sabah", "SMK Kota Kinabalu"]},
        {"amenity": "fuel", "category": "transport", "names": ["Petronas", "Shell", "BHP Petrol"]},
    ]
    
    pois = []
    
    for i in range(count):
        template = poi_templates[i % len(poi_templates)]
        name = random.choice(template["names"])
        
        # Generate coordinates in a circle around the property
        angle = (i / count) * 2 * 3.14159
        distance_km = random.uniform(0.5, 2.0)
        
        # Convert to coordinate offsets (more accurate calculation)
        lat_offset = (distance_km / 111.0) * math.cos(angle)
        lon_offset = (distance_km / (111.0 * math.cos(math.radians(lat)))) * math.sin(angle)
        
        poi_lat = lat + lat_offset
        poi_lon = lon + lon_offset
        
        # Validate coordinates
        if is_land_coordinate(poi_lat, poi_lon):
            distance = calculate_distance(lat, lon, poi_lat, poi_lon)
            
            pois.append({
                'id': f"algo_poi_{start_id + i}",
                'lat': round(poi_lat, 7),  # ✅ More precise coordinates
                'lon': round(poi_lon, 7),  # ✅ More precise coordinates
                'name': name,
                'amenity': template["amenity"],
                'category': template["category"],
                'distance': round(distance, 2),
                'tags': {
                    'amenity': template["amenity"],
                    'name': name,
                    'generated': 'true'
                }
            })
        else:
            # Simple fallback near the property
            pois.append({
                'id': f"safe_poi_{start_id + i}",
                'lat': round(lat + random.uniform(-0.001, 0.001), 7),  # ✅ More precise coordinates
                'lon': round(lon + random.uniform(-0.001, 0.001), 7),  # ✅ More precise coordinates
                'name': name,
                'amenity': template["amenity"],
                'category': template["category"],
                'distance': 0.1,
                'tags': {
                    'amenity': template["amenity"],
                    'name': name,
                    'fallback': 'safe'
                }
            })
    
    return pois

def generate_fallback_pois(lat: float, lon: float, limit: int = 20) -> List[dict]:
    """
    Generate fallback POIs for Sabah locations with accurate coordinates
    """
    # Store original coordinates for distance calculations
    original_lat, original_lon = lat, lon
    
    # Check if the property location itself is too close to water
    if 5.9 <= lat <= 6.1 and 116.0 <= lon <= 116.2:
        # If property is in coastal area, use a safer inland location for POI generation
        if lat > 5.98 and lon > 116.08:  # Property is too close to eastern water
            # Use a safer inland location for POI generation
            safe_lat = 5.97  # Move inland
            safe_lon = 116.06  # Move inland
            print(f"Property at [{lat}, {lon}] is too close to water, using safer location [{safe_lat}, {safe_lon}] for POI generation")
            lat, lon = safe_lat, safe_lon
        elif lat < 5.96 and lon > 116.03:  # Property is too close to southwestern water
            # Use a safer inland location for POI generation
            safe_lat = 5.97  # Move inland
            safe_lon = 116.05  # Move inland
            print(f"Property at [{lat}, {lon}] is too close to water, using safer location [{safe_lat}, {safe_lon}] for POI generation")
            lat, lon = safe_lat, safe_lon
    kk_realistic_pois = [
        # Food & Dining (actual areas)
        {"name": "Gaya Street Sunday Market", "amenity": "food_court", "category": "food", "lat": 5.9785, "lon": 116.0705},
        {"name": "Jesselton Point Seafood", "amenity": "restaurant", "category": "food", "lat": 5.9825, "lon": 116.0695},
        {"name": "Centre Point Food Court", "amenity": "food_court", "category": "food", "lat": 5.9820, "lon": 116.0735},
        {"name": "Suria Sabah Food Court", "amenity": "food_court", "category": "food", "lat": 5.9745, "lon": 116.0728},
        {"name": "Signal Hill Cafe", "amenity": "cafe", "category": "food", "lat": 5.9650, "lon": 116.0850},
        {"name": "Likas Square Restaurant", "amenity": "restaurant", "category": "food", "lat": 5.9890, "lon": 116.0740},
        
        # Shopping (major malls and areas)
        {"name": "Suria Sabah Shopping Mall", "amenity": "mall", "category": "shopping", "lat": 5.9745, "lon": 116.0728},
        {"name": "Centre Point Sabah", "amenity": "mall", "category": "shopping", "lat": 5.9820, "lon": 116.0735},
        {"name": "Imago Shopping Mall", "amenity": "mall", "category": "shopping", "lat": 5.9700, "lon": 116.0650},
        {"name": "Warisan Square", "amenity": "mall", "category": "shopping", "lat": 5.9815, "lon": 116.0725},
        {"name": "KK Plaza", "amenity": "mall", "category": "shopping", "lat": 5.9790, "lon": 116.0720},
        
        # Health & Medical
        {"name": "Queen Elizabeth Hospital", "amenity": "hospital", "category": "health", "lat": 5.9887, "lon": 116.0637},
        {"name": "Sabah Medical Centre", "amenity": "hospital", "category": "health", "lat": 5.9950, "lon": 116.0680},
        {"name": "Likas Women & Children Hospital", "amenity": "hospital", "category": "health", "lat": 5.9920000, "lon": 116.0750000},
        {"name": "Guardian Pharmacy Centre Point", "amenity": "pharmacy", "category": "health", "lat": 5.9820, "lon": 116.0730},
        {"name": "Watsons Suria Sabah", "amenity": "pharmacy", "category": "health", "lat": 5.9745, "lon": 116.0725},
        
        # Education
        {"name": "SK Kepayan", "amenity": "school", "category": "education", "lat": 5.9650000, "lon": 116.0750000},
        {"name": "SMK Likas", "amenity": "school", "category": "education", "lat": 5.9900000, "lon": 116.0750000},
        {"name": "Universiti Malaysia Sabah", "amenity": "university", "category": "education", "lat": 6.0350000, "lon": 116.1200000},
        {"name": "Sabah State Library", "amenity": "library", "category": "education", "lat": 5.9800000, "lon": 116.0740000},
        
        # Banking
        {"name": "Maybank Centre Point", "amenity": "bank", "category": "banking", "lat": 5.9818, "lon": 116.0733},
        {"name": "CIMB Bank Gaya Street", "amenity": "bank", "category": "banking", "lat": 5.9785, "lon": 116.0710},
        {"name": "Public Bank Suria Sabah", "amenity": "bank", "category": "banking", "lat": 5.9745, "lon": 116.0730},
        {"name": "Hong Leong Bank KK", "amenity": "bank", "category": "banking", "lat": 5.9800, "lon": 116.0715},
        {"name": "RHB Bank ATM", "amenity": "atm", "category": "banking", "lat": 5.9790, "lon": 116.0725},
        
        # Transport
        {"name": "Wawasan Bus Terminal", "amenity": "bus_station", "category": "transport", "lat": 5.9830, "lon": 116.0650},
        {"name": "Jesselton Point Ferry Terminal", "amenity": "ferry_terminal", "category": "transport", "lat": 5.9825, "lon": 116.0695},
        {"name": "Petronas Station Lintas", "amenity": "fuel", "category": "transport", "lat": 5.9700, "lon": 116.0800},
        {"name": "Shell Station Likas", "amenity": "fuel", "category": "transport", "lat": 5.9880, "lon": 116.0720},
    ]
    
    # Filter POIs that are within reasonable distance (5km) from the property
    nearby_pois = []
    for poi in kk_realistic_pois:
        distance = calculate_distance(original_lat, original_lon, poi["lat"], poi["lon"])
        if distance <= 5.0:  # Within 5km
            poi_copy = poi.copy()
            poi_copy["distance"] = round(distance, 2)
            poi_copy["id"] = f"realistic_poi_{len(nearby_pois)}"
            poi_copy["tags"] = {
                "amenity": poi["amenity"],
                "name": poi["name"],
                "realistic": "true"
            }
            nearby_pois.append(poi_copy)
    
    # If we don't have enough realistic POIs, generate additional ones with improved algorithm
    if len(nearby_pois) < limit:
        additional_needed = limit - len(nearby_pois)
        generated_pois = generate_algorithmic_pois(lat, lon, additional_needed, len(nearby_pois))
        nearby_pois.extend(generated_pois)
    
    # Sort by distance and limit results
    nearby_pois.sort(key=lambda x: x['distance'])
    return nearby_pois[:limit]

def categorize_poi(amenity: str, name: str = "") -> str:
    """Enhanced categorization for Malaysian/Sabah POIs"""
    # Comprehensive amenity lists
    food_amenities = [
        'restaurant', 'cafe', 'fast_food', 'food_court', 'bar', 'pub', 'food', 'dining',
        'bbq', 'ice_cream', 'drinking_water', 'cuisine'
    ]
    health_amenities = [
        'hospital', 'clinic', 'pharmacy', 'dentist', 'veterinary', 'medical', 'health',
        'doctors', 'health_centre', 'medical_centre', 'healthcare'
    ]
    education_amenities = [
        'school', 'college', 'university', 'kindergarten', 'library', 'education',
        'research_institute', 'training'
    ]
    shopping_amenities = [
        'supermarket', 'mall', 'convenience', 'marketplace', 'shop', 'mini_market', 
        'grocery', 'hypermarket', 'department_store', 'shopping_mall', 'retail'
    ]
    banking_amenities = [
        'bank', 'atm', 'bureau_de_change', 'banking', 'financial'
    ]
    transport_amenities = [
        'bus_station', 'taxi', 'subway_entrance', 'ferry_terminal', 'fuel', 'parking', 
        'transport', 'bus_stop', 'airport'
    ]
    tourism_amenities = [
        'hotel', 'hostel', 'motel', 'guest_house', 'resort', 'accommodation', 'tourism',
        'attraction', 'museum', 'gallery', 'park', 'playground', 'garden'
    ]
    
    # Check amenity first
    if amenity in food_amenities:
        return 'food'
    elif amenity in health_amenities:
        return 'health'
    elif amenity in education_amenities:
        return 'education'
    elif amenity in shopping_amenities:
        return 'shopping'
    elif amenity in banking_amenities:
        return 'banking'
    elif amenity in transport_amenities:
        return 'transport'
    elif amenity in tourism_amenities:
        return 'transport'  # Map tourism to transport for now since we only have 6 categories
    
    # If amenity doesn't match, check name for common patterns
    name_lower = name.lower()
    
    # Enhanced shopping patterns (Malaysian/Sabah specific)
    shopping_patterns = [
        'mart', 'market', 'supermarket', 'hypermarket', 'grocery', 'convenience',
        'speedmart', 'emart', 'giant', 'tesco', 'aeon', 'parkson', 'mall',
        'shopping', 'department', 'store', 'shop', 'mini market', 'mini_market',
        '7-eleven', 'family mart', 'watsons', 'guardian', 'pharmacy', 'kedai',
        'kedai runcit', 'kedai runcit', 'servay', 'bataras', 'btc', 'pei lee'
    ]
    
    # Enhanced food patterns (Malaysian/Sabah specific)
    food_patterns = [
        'restaurant', 'cafe', 'kopitiam', 'food', 'bbq', 'pizza', 'hut',
        'delivery', 'fast food', 'fast_food', 'bar', 'pub', 'kitchen',
        'mcdonalds', 'kfc', 'burger king', 'pizza hut', 'starbucks', 'coffee',
        'nasi lemak', 'mee', 'laksa', 'dim sum', 'seafood', 'restoran',
        'warung', 'kedai makan', 'canteen', 'hawker', 'stall'
    ]
    
    # Enhanced health patterns (Malaysian/Sabah specific)
    health_patterns = [
        'clinic', 'hospital', 'pharmacy', 'medical', 'health', 'care',
        'specialist', 'dental', 'veterinary', 'animal', 'watsons', 'guardian',
        'poliklinik', 'klinik', 'rumah sakit', 'pusat kesihatan', 'medical centre'
    ]
    
    # Education patterns (Malaysian/Sabah specific) - more specific to avoid false matches
    education_patterns = [
        'school', 'sekolah', 'sk ', 'smk ', 'university', 'universiti', 'college',
        'library', 'perpustakaan', 'tadika', 'kindergarten', 'training', 'institute',
        'academy', 'learning', 'education', 'student', 'campus', 'maktab'
    ]
    
    # Banking patterns (Malaysian specific)
    banking_patterns = [
        'bank', 'atm', 'maybank', 'cimb', 'public bank', 'hong leong', 'rhb',
        'ambank', 'bsn', 'bank negara', 'muamalat', 'affin', 'alliance'
    ]
    
    # Transport patterns (Malaysian specific)
    transport_patterns = [
        'bus', 'station', 'terminal', 'ferry', 'taxi', 'petronas', 'shell',
        'bhp', 'caltex', 'parking', 'fuel', 'gas station', 'bas', 'teksi',
        'hotel', 'hostel', 'motel', 'resort', 'accommodation', 'park', 'garden'
    ]
    
    # Check name patterns for all categories - be more strict to avoid false matches
    # Only use name patterns if amenity is not clearly defined
    if amenity in ['other', 'unknown', ''] or amenity is None:
        for pattern in shopping_patterns:
            if pattern in name_lower:
                return 'shopping'
        
        for pattern in food_patterns:
            if pattern in name_lower:
                return 'food'
        
        for pattern in health_patterns:
            if pattern in name_lower:
                return 'health'
        
        for pattern in education_patterns:
            if pattern in name_lower:
                return 'education'
        
        for pattern in banking_patterns:
            if pattern in name_lower:
                return 'banking'
        
        for pattern in transport_patterns:
            if pattern in name_lower:
                return 'transport'
    
    return 'other'

@router.get("/education-pois")
async def get_education_pois(request: Request, lat: float, lon: float, radius: int = 15000, limit: int = 50):
    """
    Get educational POIs (schools, kindergartens, colleges, universities) near a location
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        print(f"Fetching educational POIs for location [{lat}, {lon}] with radius {radius}m")
        
        # Validate inputs
        if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
            raise HTTPException(status_code=400, detail="Invalid coordinates")
        
        # Simplified Overpass API query for educational institutions
        overpass_query = f"""
        [out:json][timeout:15];
        (
          // Educational institutions - simplified query
          node["amenity"~"^(school|college|university|kindergarten|library|research_institute|training|education)$"](around:{radius},{lat},{lon});
          node["education"](around:{radius},{lat},{lon});
        );
        out center;
        """
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://overpass-api.de/api/interpreter",
                data=overpass_query,
                headers={"Content-Type": "text/plain"},
                timeout=30.0
            )
            
            print(f"Overpass API response status: {response.status_code}")
            if response.status_code == 200:
                data = response.json()
                print(f"Overpass API returned data with {len(data.get('elements', []))} elements")
                
                if data and "elements" in data:
                    pois = []
                    seen_pois = set()
                    
                    for element in data["elements"]:
                        if element.get("lat") and element.get("lon"):
                            poi_lat = float(element["lat"])
                            poi_lon = float(element["lon"])
                            
                            # Calculate distance using geopy
                            distance = geodesic((lat, lon), (poi_lat, poi_lon)).kilometers
                            
                            # Skip if too far
                            if distance > (radius / 1000):
                                continue
                            
                            # Extract name and amenity
                            tags = element.get("tags", {})
                            name = tags.get("name") or tags.get("brand") or tags.get("operator") or tags.get("description") or "Educational Institution"
                            amenity = tags.get("amenity") or tags.get("education") or "education"
                            
                            # Create unique key for deduplication
                            poi_key = f"{name}_{poi_lat}_{poi_lon}"
                            if poi_key in seen_pois:
                                continue
                            seen_pois.add(poi_key)
                            
                            # Determine education type
                            education_type = categorize_education_type(amenity, name)
                            
                            pois.append({
                                "id": f"edu_{element.get('id', 'unknown')}",
                                "lat": poi_lat,
                                "lon": poi_lon,
                                "name": name,
                                "amenity": amenity,
                                "education_type": education_type,
                                "distance": round(distance, 2),
                                "tags": tags,
                                "source": "real_osm_education"
                            })
                    
                    # Sort by distance and limit results
                    pois.sort(key=lambda x: x["distance"])
                    pois = pois[:limit]
                    
                    # Log results
                    education_types = {}
                    for poi in pois:
                        edu_type = poi["education_type"]
                        education_types[edu_type] = education_types.get(edu_type, 0) + 1
                    
                    print(f"✅ Returning {len(pois)} educational POIs from OpenStreetMap")
                    print(f"📊 Education types: {education_types}")
                    return pois
                else:
                    print("❌ Overpass API returned empty data")
            else:
                print(f"❌ Overpass API failed with status {response.status_code}: {response.text}")
        
        # No fallback - return empty list if Overpass fails
        print("❌ Overpass API failed - no educational POI data available")
        return []
        
    except Exception as e:
        print(f"❌ Educational POI endpoint error: {e}")
        return []

def categorize_education_type(amenity: str, name: str = "") -> str:
    """Categorize educational institutions by type"""
    name_lower = name.lower()
    
    # University level
    if amenity == 'university' or 'university' in name_lower or 'universiti' in name_lower:
        return 'university'
    
    # College level
    if amenity == 'college' or 'college' in name_lower or 'maktab' in name_lower:
        return 'college'
    
    # Kindergarten/Preschool
    if amenity == 'kindergarten' or 'kindergarten' in name_lower or 'tadika' in name_lower:
        return 'kindergarten'
    
    # Library
    if amenity == 'library' or 'library' in name_lower or 'perpustakaan' in name_lower:
        return 'library'
    
    # Research Institute
    if amenity == 'research_institute' or 'research' in name_lower or 'institute' in name_lower:
        return 'research_institute'
    
    # Training Center
    if amenity == 'training' or 'training' in name_lower or 'academy' in name_lower:
        return 'training'
    
    # School (default for school amenity)
    if amenity == 'school' or 'school' in name_lower or 'sekolah' in name_lower or 'sk' in name_lower or 'smk' in name_lower:
        return 'school'
    
    # Madrasah (Islamic school)
    if 'madrasah' in name_lower:
        return 'madrasah'
    
    return 'school'  # Default fallback

@router.get("/bulk-education-pois")
async def get_bulk_education_pois(request: Request, coordinates: str):
    """
    Get educational POIs for multiple property locations efficiently
    Format: coordinates="lat1,lon1|lat2,lon2|lat3,lon3"
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        # Parse coordinates
        coord_pairs = coordinates.split('|')
        if len(coord_pairs) > 20:  # Limit to 20 properties at once
            raise HTTPException(status_code=400, detail="Maximum 20 properties per request")
        
        results = []
        for coord_pair in coord_pairs:
            try:
                lat_str, lon_str = coord_pair.split(',')
                lat, lon = float(lat_str), float(lon_str)
                
                # Validate coordinates
                if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
                    continue
                
                # Get educational POIs for this location
                pois = await get_education_pois_internal(lat, lon, 15000, 30)
                results.append({
                    "coordinates": [lat, lon],
                    "educational_pois": pois,
                    "count": len(pois)
                })
                
            except ValueError:
                continue
        
        return {
            "message": f"Retrieved educational POIs for {len(results)} locations",
            "results": results,
            "total_educational_pois": sum(r["count"] for r in results)
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Bulk educational POI error: {str(e)}")

async def get_education_pois_internal(lat: float, lon: float, radius: int = 15000, limit: int = 50):
    """
    Internal function to get educational POIs without rate limiting (for bulk operations)
    """
    try:
        # Simplified Overpass API query for educational institutions
        overpass_query = f"""
        [out:json][timeout:15];
        (
          // Educational institutions - simplified query
          node["amenity"~"^(school|college|university|kindergarten|library|research_institute|training|education)$"](around:{radius},{lat},{lon});
          node["education"](around:{radius},{lat},{lon});
        );
        out center;
        """
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://overpass-api.de/api/interpreter",
                data=overpass_query,
                headers={"Content-Type": "text/plain"},
                timeout=30.0
            )
            
            if response.status_code == 200:
                data = response.json()
                
                if data and "elements" in data:
                    pois = []
                    seen_pois = set()
                    
                    for element in data["elements"]:
                        if element.get("lat") and element.get("lon"):
                            poi_lat = float(element["lat"])
                            poi_lon = float(element["lon"])
                            
                            distance = geodesic((lat, lon), (poi_lat, poi_lon)).kilometers
                            if distance > (radius / 1000):
                                continue
                            
                            tags = element.get("tags", {})
                            name = tags.get("name") or tags.get("brand") or tags.get("operator") or tags.get("description") or "Educational Institution"
                            amenity = tags.get("amenity") or tags.get("education") or "education"
                            
                            poi_key = f"{name}_{poi_lat}_{poi_lon}"
                            if poi_key in seen_pois:
                                continue
                            seen_pois.add(poi_key)
                            
                            education_type = categorize_education_type(amenity, name)
                            
                            pois.append({
                                "id": f"edu_{element.get('id', 'unknown')}",
                                "lat": poi_lat,
                                "lon": poi_lon,
                                "name": name,
                                "amenity": amenity,
                                "education_type": education_type,
                                "distance": round(distance, 2),
                                "tags": tags,
                                "source": "real_osm_education"
                            })
                    
                    pois.sort(key=lambda x: x["distance"])
                    return pois[:limit]
            
            return []
            
    except Exception as e:
        print(f"❌ Internal educational POI fetch error: {e}")
        return []

@router.get("/health-pois")
async def get_health_pois(request: Request, lat: float, lon: float, radius: int = 15000, limit: int = 50):
    """
    Get health POIs (hospitals, clinics, pharmacies, veterinarians) near a location
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        print(f"Fetching health POIs for location [{lat}, {lon}] with radius {radius}m")
        
        # Validate inputs
        if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
            raise HTTPException(status_code=400, detail="Invalid coordinates")
        
        # Simplified Overpass API query for health institutions
        overpass_query = f"""
        [out:json][timeout:15];
        (
          // Health institutions - simplified query
          node["amenity"~"^(hospital|clinic|pharmacy|dentist|veterinary|doctors|health_centre|medical_centre|healthcare)$"](around:{radius},{lat},{lon});
          node["healthcare"](around:{radius},{lat},{lon});
        );
        out center;
        """
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://overpass-api.de/api/interpreter",
                data=overpass_query,
                headers={"Content-Type": "text/plain"},
                timeout=30.0
            )
            
            print(f"Overpass API response status: {response.status_code}")
            if response.status_code == 200:
                data = response.json()
                print(f"Overpass API returned data with {len(data.get('elements', []))} elements")
                
                if data and "elements" in data:
                    pois = []
                    seen_pois = set()
                    
                    for element in data["elements"]:
                        if element.get("lat") and element.get("lon"):
                            poi_lat = float(element["lat"])
                            poi_lon = float(element["lon"])
                            
                            # Calculate distance using geopy
                            distance = geodesic((lat, lon), (poi_lat, poi_lon)).kilometers
                            
                            # Skip if too far
                            if distance > (radius / 1000):
                                continue
                            
                            # Extract name and amenity
                            tags = element.get("tags", {})
                            name = tags.get("name") or tags.get("brand") or tags.get("operator") or tags.get("description") or "Health Facility"
                            amenity = tags.get("amenity") or tags.get("healthcare") or "healthcare"
                            
                            # Create unique key for deduplication
                            poi_key = f"{name}_{poi_lat}_{poi_lon}"
                            if poi_key in seen_pois:
                                continue
                            seen_pois.add(poi_key)
                            
                            # Determine health type
                            health_type = categorize_health_type(amenity, name)
                            
                            pois.append({
                                "id": f"health_{element.get('id', 'unknown')}",
                                "lat": poi_lat,
                                "lon": poi_lon,
                                "name": name,
                                "amenity": amenity,
                                "health_type": health_type,
                                "distance": round(distance, 2),
                                "tags": tags,
                                "source": "real_osm_health"
                            })
                    
                    # Sort by distance and limit results
                    pois.sort(key=lambda x: x["distance"])
                    pois = pois[:limit]
                    
                    # Log results
                    health_types = {}
                    for poi in pois:
                        health_type = poi["health_type"]
                        health_types[health_type] = health_types.get(health_type, 0) + 1
                    
                    print(f"✅ Returning {len(pois)} health POIs from OpenStreetMap")
                    print(f"📊 Health types: {health_types}")
                    return pois
                else:
                    print("❌ Overpass API returned empty data")
            else:
                print(f"❌ Overpass API failed with status {response.status_code}: {response.text}")
        
        # No fallback - return empty list if Overpass fails
        print("❌ Overpass API failed - no health POI data available")
        return []
        
    except Exception as e:
        print(f"❌ Health POI endpoint error: {e}")
        return []

def categorize_health_type(amenity: str, name: str = "") -> str:
    """Categorize health institutions by type"""
    name_lower = name.lower()
    
    # Hospital level
    if amenity == 'hospital' or 'hospital' in name_lower or 'rumah sakit' in name_lower:
        return 'hospital'
    
    # Clinic level
    if amenity == 'clinic' or 'clinic' in name_lower or 'klinik' in name_lower or 'poliklinik' in name_lower:
        return 'clinic'
    
    # Pharmacy
    if amenity == 'pharmacy' or 'pharmacy' in name_lower or 'watsons' in name_lower or 'guardian' in name_lower:
        return 'pharmacy'
    
    # Dental
    if amenity == 'dentist' or 'dental' in name_lower or 'dentist' in name_lower:
        return 'dental'
    
    # Veterinary
    if amenity == 'veterinary' or 'veterinary' in name_lower or 'animal' in name_lower or 'vet' in name_lower:
        return 'veterinary'
    
    # Medical Centre
    if 'medical centre' in name_lower or 'health centre' in name_lower or 'pusat kesihatan' in name_lower:
        return 'medical_centre'
    
    # Doctors
    if amenity == 'doctors' or 'specialist' in name_lower or 'doctor' in name_lower:
        return 'doctors'
    
    return 'clinic'  # Default fallback

@router.get("/bulk-health-pois")
async def get_bulk_health_pois(request: Request, coordinates: str):
    """
    Get health POIs for multiple property locations efficiently
    Format: coordinates="lat1,lon1|lat2,lon2|lat3,lon3"
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        # Parse coordinates
        coord_pairs = coordinates.split('|')
        if len(coord_pairs) > 20:  # Limit to 20 properties at once
            raise HTTPException(status_code=400, detail="Maximum 20 properties per request")
        
        results = []
        for coord_pair in coord_pairs:
            try:
                lat_str, lon_str = coord_pair.split(',')
                lat, lon = float(lat_str), float(lon_str)
                
                # Validate coordinates
                if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
                    continue
                
                # Get health POIs for this location
                pois = await get_health_pois_internal(lat, lon, 15000, 30)
                results.append({
                    "coordinates": [lat, lon],
                    "health_pois": pois,
                    "count": len(pois)
                })
                
            except ValueError:
                continue
        
        return {
            "message": f"Retrieved health POIs for {len(results)} locations",
            "results": results,
            "total_health_pois": sum(r["count"] for r in results)
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Bulk health POI error: {str(e)}")

async def get_health_pois_internal(lat: float, lon: float, radius: int = 15000, limit: int = 50):
    """
    Internal function to get health POIs without rate limiting (for bulk operations)
    """
    try:
        # Simplified Overpass API query for health institutions
        overpass_query = f"""
        [out:json][timeout:15];
        (
          // Health institutions - simplified query
          node["amenity"~"^(hospital|clinic|pharmacy|dentist|veterinary|doctors|health_centre|medical_centre|healthcare)$"](around:{radius},{lat},{lon});
          node["healthcare"](around:{radius},{lat},{lon});
        );
        out center;
        """
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://overpass-api.de/api/interpreter",
                data=overpass_query,
                headers={"Content-Type": "text/plain"},
                timeout=30.0
            )
            
            if response.status_code == 200:
                data = response.json()
                
                if data and "elements" in data:
                    pois = []
                    seen_pois = set()
                    
                    for element in data["elements"]:
                        if element.get("lat") and element.get("lon"):
                            poi_lat = float(element["lat"])
                            poi_lon = float(element["lon"])
                            
                            distance = geodesic((lat, lon), (poi_lat, poi_lon)).kilometers
                            if distance > (radius / 1000):
                                continue
                            
                            tags = element.get("tags", {})
                            name = tags.get("name") or tags.get("brand") or tags.get("operator") or tags.get("description") or "Health Facility"
                            amenity = tags.get("amenity") or tags.get("healthcare") or "healthcare"
                            
                            poi_key = f"{name}_{poi_lat}_{poi_lon}"
                            if poi_key in seen_pois:
                                continue
                            seen_pois.add(poi_key)
                            
                            health_type = categorize_health_type(amenity, name)
                            
                            pois.append({
                                "id": f"health_{element.get('id', 'unknown')}",
                                "lat": poi_lat,
                                "lon": poi_lon,
                                "name": name,
                                "amenity": amenity,
                                "health_type": health_type,
                                "distance": round(distance, 2),
                                "tags": tags,
                                "source": "real_osm_health"
                            })
                    
                    pois.sort(key=lambda x: x["distance"])
                    return pois[:limit]
            
            return []
            
    except Exception as e:
        print(f"❌ Internal health POI fetch error: {e}")
        return []

@router.get("/shopping-pois")
async def get_shopping_pois(request: Request, lat: float, lon: float, radius: int = 15000, limit: int = 50):
    """
    Get shopping POIs (malls, supermarkets, plazas, hypermarkets, grocers) near a location
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        print(f"Fetching shopping POIs for location [{lat}, {lon}] with radius {radius}m")
        
        # Validate inputs
        if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
            raise HTTPException(status_code=400, detail="Invalid coordinates")
        
        # Simplified Overpass API query for shopping institutions
        overpass_query = f"""
        [out:json][timeout:15];
        (
          // Shopping institutions - simplified query
          node["shop"](around:{radius},{lat},{lon});
          node["amenity"~"^(marketplace|market)$"](around:{radius},{lat},{lon});
        );
        out center;
        """
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://overpass-api.de/api/interpreter",
                data=overpass_query,
                headers={"Content-Type": "text/plain"},
                timeout=30.0
            )
            
            print(f"Overpass API response status: {response.status_code}")
            if response.status_code == 200:
                data = response.json()
                print(f"Overpass API returned data with {len(data.get('elements', []))} elements")
                
                if data and "elements" in data:
                    pois = []
                    seen_pois = set()
                    
                    for element in data["elements"]:
                        if element.get("lat") and element.get("lon"):
                            poi_lat = float(element["lat"])
                            poi_lon = float(element["lon"])
                            
                            # Calculate distance using geopy
                            distance = geodesic((lat, lon), (poi_lat, poi_lon)).kilometers
                            
                            # Skip if too far
                            if distance > (radius / 1000):
                                continue
                            
                            # Extract name and amenity
                            tags = element.get("tags", {})
                            name = tags.get("name") or tags.get("brand") or tags.get("operator") or tags.get("description") or "Shopping Facility"
                            amenity = tags.get("shop") or tags.get("amenity") or "shop"
                            
                            # Create unique key for deduplication
                            poi_key = f"{name}_{poi_lat}_{poi_lon}"
                            if poi_key in seen_pois:
                                continue
                            seen_pois.add(poi_key)
                            
                            # Determine shopping type
                            shopping_type = categorize_shopping_type(amenity, name)
                            
                            pois.append({
                                "id": f"shopping_{element.get('id', 'unknown')}",
                                "lat": poi_lat,
                                "lon": poi_lon,
                                "name": name,
                                "amenity": amenity,
                                "shopping_type": shopping_type,
                                "distance": round(distance, 2),
                                "tags": tags,
                                "source": "real_osm_shopping"
                            })
                    
                    # Sort by distance and limit results
                    pois.sort(key=lambda x: x["distance"])
                    pois = pois[:limit]
                    
                    # Log results
                    shopping_types = {}
                    for poi in pois:
                        shopping_type = poi["shopping_type"]
                        shopping_types[shopping_type] = shopping_types.get(shopping_type, 0) + 1
                    
                    print(f"✅ Returning {len(pois)} shopping POIs from OpenStreetMap")
                    print(f"📊 Shopping types: {shopping_types}")
                    return pois
                else:
                    print("❌ Overpass API returned empty data")
            else:
                print(f"❌ Overpass API failed with status {response.status_code}: {response.text}")
        
        # No fallback - return empty list if Overpass fails
        print("❌ Overpass API failed - no shopping POI data available")
        return []
        
    except Exception as e:
        print(f"❌ Shopping POI endpoint error: {e}")
        return []

def categorize_shopping_type(amenity: str, name: str = "") -> str:
    """Categorize shopping institutions by type"""
    name_lower = name.lower()
    
    # Mall level
    if amenity == 'mall' or 'mall' in name_lower or 'shopping mall' in name_lower or 'shopping centre' in name_lower:
        return 'mall'
    
    # Hypermarket
    if amenity == 'hypermarket' or 'hypermarket' in name_lower or 'hyper' in name_lower:
        return 'hypermarket'
    
    # Supermarket
    if amenity == 'supermarket' or 'supermarket' in name_lower or 'super' in name_lower:
        return 'supermarket'
    
    # Plaza
    if 'plaza' in name_lower or 'centre' in name_lower or 'center' in name_lower:
        return 'plaza'
    
    # Department Store
    if amenity == 'department_store' or 'department' in name_lower:
        return 'department_store'
    
    # Convenience Store
    if amenity == 'convenience' or 'convenience' in name_lower or 'mini market' in name_lower or 'mini_market' in name_lower:
        return 'convenience'
    
    # Grocery
    if amenity == 'grocery' or 'grocery' in name_lower or 'grocer' in name_lower:
        return 'grocery'
    
    # Marketplace
    if amenity == 'marketplace' or amenity == 'market' or 'market' in name_lower:
        return 'marketplace'
    
    # General Store
    if amenity == 'store' or 'store' in name_lower or 'shop' in name_lower:
        return 'store'
    
    return 'store'  # Default fallback

@router.get("/bulk-shopping-pois")
async def get_bulk_shopping_pois(request: Request, coordinates: str):
    """
    Get shopping POIs for multiple property locations efficiently
    Format: coordinates="lat1,lon1|lat2,lon2|lat3,lon3"
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        # Parse coordinates
        coord_pairs = coordinates.split('|')
        if len(coord_pairs) > 20:  # Limit to 20 properties at once
            raise HTTPException(status_code=400, detail="Maximum 20 properties per request")
        
        results = []
        for coord_pair in coord_pairs:
            try:
                lat_str, lon_str = coord_pair.split(',')
                lat, lon = float(lat_str), float(lon_str)
                
                # Validate coordinates
                if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
                    continue
                
                # Get shopping POIs for this location
                pois = await get_shopping_pois_internal(lat, lon, 15000, 30)
                results.append({
                    "coordinates": [lat, lon],
                    "shopping_pois": pois,
                    "count": len(pois)
                })
                
            except ValueError:
                continue
        
        return {
            "message": f"Retrieved shopping POIs for {len(results)} locations",
            "results": results,
            "total_shopping_pois": sum(r["count"] for r in results)
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Bulk shopping POI error: {str(e)}")

async def get_shopping_pois_internal(lat: float, lon: float, radius: int = 15000, limit: int = 50):
    """
    Internal function to get shopping POIs without rate limiting (for bulk operations)
    """
    try:
        # Simplified Overpass API query for shopping institutions
        overpass_query = f"""
        [out:json][timeout:15];
        (
          // Shopping institutions - simplified query
          node["shop"](around:{radius},{lat},{lon});
          node["amenity"~"^(marketplace|market)$"](around:{radius},{lat},{lon});
        );
        out center;
        """
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://overpass-api.de/api/interpreter",
                data=overpass_query,
                headers={"Content-Type": "text/plain"},
                timeout=30.0
            )
            
            if response.status_code == 200:
                data = response.json()
                
                if data and "elements" in data:
                    pois = []
                    seen_pois = set()
                    
                    for element in data["elements"]:
                        if element.get("lat") and element.get("lon"):
                            poi_lat = float(element["lat"])
                            poi_lon = float(element["lon"])
                            
                            distance = geodesic((lat, lon), (poi_lat, poi_lon)).kilometers
                            if distance > (radius / 1000):
                                continue
                            
                            tags = element.get("tags", {})
                            name = tags.get("name") or tags.get("brand") or tags.get("operator") or tags.get("description") or "Shopping Facility"
                            amenity = tags.get("shop") or tags.get("amenity") or "shop"
                            
                            poi_key = f"{name}_{poi_lat}_{poi_lon}"
                            if poi_key in seen_pois:
                                continue
                            seen_pois.add(poi_key)
                            
                            shopping_type = categorize_shopping_type(amenity, name)
                            
                            pois.append({
                                "id": f"shopping_{element.get('id', 'unknown')}",
                                "lat": poi_lat,
                                "lon": poi_lon,
                                "name": name,
                                "amenity": amenity,
                                "shopping_type": shopping_type,
                                "distance": round(distance, 2),
                                "tags": tags,
                                "source": "real_osm_shopping"
                            })
                    
                    pois.sort(key=lambda x: x["distance"])
                    return pois[:limit]
            
            return []
            
    except Exception as e:
        print(f"❌ Internal shopping POI fetch error: {e}")
        return []

@router.get("/property-shopping-pois/{property_id}")
async def get_property_shopping_pois(request: Request, property_id: int):
    """
    Get shopping POIs for a specific property from the database
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        # Query the database for shopping POIs for this property
        from sqlalchemy import text
        from db.db_connection import SessionLocal
        
        db = SessionLocal()
        try:
            result = db.execute(text("""
                SELECT 
                    sp.poi_id,
                    sp.name,
                    sp.amenity,
                    sp.shopping_type,
                    sp.latitude,
                    sp.longitude,
                    sp.distance_km,
                    sp.tags,
                    sp.source,
                    h.title as property_title,
                    h.latitude as property_lat,
                    h.longitude as property_lon
                FROM shopping_pois sp
                JOIN houses h ON sp.property_id = h.id
                WHERE sp.property_id = :property_id
                ORDER BY sp.distance_km ASC
            """), {"property_id": property_id})
            
            pois = result.fetchall()
            
            if not pois:
                return {
                    "property_id": property_id,
                    "property_title": "Unknown",
                    "shopping_pois": [],
                    "count": 0,
                    "message": "No shopping POIs found for this property"
                }
            
            # Convert to list of dictionaries
            shopping_pois = []
            for poi in pois:
                shopping_pois.append({
                    "id": poi.poi_id,
                    "name": poi.name,
                    "amenity": poi.amenity,
                    "shopping_type": poi.shopping_type,
                    "lat": float(poi.latitude),
                    "lon": float(poi.longitude),
                    "distance": float(poi.distance_km),
                    "tags": poi.tags if isinstance(poi.tags, dict) else (json.loads(poi.tags) if poi.tags else {}),
                    "source": poi.source
                })
            
            return {
                "property_id": property_id,
                "property_title": pois[0].property_title,
                "property_coordinates": [float(pois[0].property_lat), float(pois[0].property_lon)],
                "shopping_pois": shopping_pois,
                "count": len(shopping_pois),
                "message": f"Found {len(shopping_pois)} shopping POIs for {pois[0].property_title}"
            }
            
        finally:
            db.close()
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error fetching shopping POIs: {str(e)}")

@router.get("/property-health-pois/{property_id}")
async def get_property_health_pois(request: Request, property_id: int):
    """
    Get health POIs for a specific property from the database
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        # Query the database for health POIs for this property
        from sqlalchemy import text
        from db.db_connection import SessionLocal
        
        db = SessionLocal()
        try:
            result = db.execute(text("""
                SELECT 
                    hp.poi_id,
                    hp.name,
                    hp.amenity,
                    hp.health_type,
                    hp.latitude,
                    hp.longitude,
                    hp.distance_km,
                    hp.tags,
                    hp.source,
                    h.title as property_title,
                    h.latitude as property_lat,
                    h.longitude as property_lon
                FROM health_pois hp
                JOIN houses h ON hp.property_id = h.id
                WHERE hp.property_id = :property_id
                ORDER BY hp.distance_km ASC
            """), {"property_id": property_id})
            
            pois = result.fetchall()
            
            if not pois:
                return {
                    "property_id": property_id,
                    "property_title": "Unknown",
                    "health_pois": [],
                    "count": 0,
                    "message": "No health POIs found for this property"
                }
            
            # Convert to list of dictionaries
            health_pois = []
            for poi in pois:
                health_pois.append({
                    "id": poi.poi_id,
                    "name": poi.name,
                    "amenity": poi.amenity,
                    "health_type": poi.health_type,
                    "lat": float(poi.latitude),
                    "lon": float(poi.longitude),
                    "distance": float(poi.distance_km),
                    "tags": poi.tags if isinstance(poi.tags, dict) else (json.loads(poi.tags) if poi.tags else {}),
                    "source": poi.source
                })
            
            return {
                "property_id": property_id,
                "property_title": pois[0].property_title,
                "property_coordinates": [float(pois[0].property_lat), float(pois[0].property_lon)],
                "health_pois": health_pois,
                "count": len(health_pois),
                "message": f"Found {len(health_pois)} health POIs for {pois[0].property_title}"
            }
            
        finally:
            db.close()
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error fetching health POIs: {str(e)}")

@router.get("/property-education-pois/{property_id}")
async def get_property_education_pois(request: Request, property_id: int):
    """
    Get educational POIs for a specific property from the database
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        # Query the database for educational POIs for this property
        from sqlalchemy import text
        from db.db_connection import SessionLocal
        
        db = SessionLocal()
        try:
            result = db.execute(text("""
                SELECT 
                    ep.poi_id,
                    ep.name,
                    ep.amenity,
                    ep.education_type,
                    ep.latitude,
                    ep.longitude,
                    ep.distance_km,
                    ep.tags,
                    ep.source,
                    h.title as property_title,
                    h.latitude as property_lat,
                    h.longitude as property_lon
                FROM educational_pois ep
                JOIN houses h ON ep.property_id = h.id
                WHERE ep.property_id = :property_id
                ORDER BY ep.distance_km ASC
            """), {"property_id": property_id})
            
            pois = result.fetchall()
            
            if not pois:
                return {
                    "property_id": property_id,
                    "property_title": "Unknown",
                    "educational_pois": [],
                    "count": 0,
                    "message": "No educational POIs found for this property"
                }
            
            # Convert to list of dictionaries
            educational_pois = []
            for poi in pois:
                educational_pois.append({
                    "id": poi.poi_id,
                    "name": poi.name,
                    "amenity": poi.amenity,
                    "education_type": poi.education_type,
                    "lat": float(poi.latitude),
                    "lon": float(poi.longitude),
                    "distance": float(poi.distance_km),
                    "tags": poi.tags if isinstance(poi.tags, dict) else (json.loads(poi.tags) if poi.tags else {}),
                    "source": poi.source
                })
            
            return {
                "property_id": property_id,
                "property_title": pois[0].property_title,
                "property_coordinates": [float(pois[0].property_lat), float(pois[0].property_lon)],
                "educational_pois": educational_pois,
                "count": len(educational_pois),
                "message": f"Found {len(educational_pois)} educational POIs for {pois[0].property_title}"
            }
            
        finally:
            db.close()
            
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error fetching educational POIs: {str(e)}")

@router.get("/bulk-pois")
async def get_bulk_pois(request: Request, coordinates: str):
    """
    Get POIs for multiple property locations efficiently
    Format: coordinates="lat1,lon1|lat2,lon2|lat3,lon3"
    """
    try:
        # Add rate limiting
        check_rate_limit(request.client.host)
        
        # Parse coordinates
        coord_pairs = coordinates.split('|')
        if len(coord_pairs) > 10:  # Limit to 10 properties at once
            raise HTTPException(status_code=400, detail="Maximum 10 properties per request")
        
        results = []
        for coord_pair in coord_pairs:
            try:
                lat_str, lon_str = coord_pair.split(',')
                lat, lon = float(lat_str), float(lon_str)
                
                # Validate coordinates
                if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
                    continue
                
                # Get POIs for this location
                pois = await get_real_pois_internal(lat, lon, 10000, 50)
                results.append({
                    "coordinates": [lat, lon],
                    "pois": pois,
                    "count": len(pois)
                })
                
            except ValueError:
                continue
        
        return {
            "message": f"Retrieved POIs for {len(results)} locations",
            "results": results,
            "total_pois": sum(r["count"] for r in results)
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Bulk POI error: {str(e)}")

async def get_real_pois_internal(lat: float, lon: float, radius: int = 10000, limit: int = 100):
    """
    Internal function to get POIs without rate limiting (for bulk operations)
    """
    try:
        # Simplified Overpass API query for better reliability
        overpass_query = f"""
        [out:json][timeout:15];
        (
          // Core amenities
          node["amenity"~"^(restaurant|cafe|fast_food|hospital|clinic|pharmacy|school|university|bank|atm|fuel|parking|bus_station)$"](around:{radius},{lat},{lon});
          node["shop"](around:{radius},{lat},{lon});
          node["tourism"](around:{radius},{lat},{lon});
          node["leisure"](around:{radius},{lat},{lon});
        );
        out center;
        """
        
        async with httpx.AsyncClient() as client:
            response = await client.post(
                "https://overpass-api.de/api/interpreter",
                data=overpass_query,
                headers={"Content-Type": "text/plain"},
                timeout=30.0
            )
            
            if response.status_code == 200:
                data = response.json()
                
                if data and "elements" in data:
                    pois = []
                    seen_pois = set()
                    
                    for element in data["elements"]:
                        if element.get("lat") and element.get("lon"):
                            poi_lat = float(element["lat"])
                            poi_lon = float(element["lon"])
                            
                            distance = geodesic((lat, lon), (poi_lat, poi_lon)).kilometers
                            if distance > (radius / 1000):
                                continue
                            
                            tags = element.get("tags", {})
                            name = tags.get("name") or tags.get("brand") or tags.get("operator") or tags.get("description") or "Local Business"
                            amenity = tags.get("amenity") or tags.get("shop") or tags.get("tourism") or tags.get("leisure") or "other"
                            
                            poi_key = f"{name}_{poi_lat}_{poi_lon}"
                            if poi_key in seen_pois:
                                continue
                            seen_pois.add(poi_key)
                            
                            category = categorize_poi(amenity, name)
                            if category in ['food', 'health', 'education', 'shopping', 'transport', 'banking']:
                                pois.append({
                                    "id": f"real_{element.get('id', 'unknown')}",
                                    "lat": poi_lat,
                                    "lon": poi_lon,
                                    "name": name,
                                    "amenity": amenity,
                                    "category": category,
                                    "distance": round(distance, 2),
                                    "tags": tags,
                                    "source": "real_osm"
                                })
                    
                    pois.sort(key=lambda x: x["distance"])
                    return pois[:limit]
        
        # No fallback - return empty list
        return []
        
    except Exception as e:
        print(f"❌ Internal POI fetch error: {e}")
        return []