"""
Geolocation Service
High-level geolocation operations using map provider abstraction
"""
from typing import Optional, List, Tuple
import logging

from sqlalchemy.ext.asyncio import AsyncSession

from app.services.map_provider import (
    MapProvider,
    GeocodingResult,
    RouteResult,
    DistanceResult,
    get_map_provider
)

logger = logging.getLogger(__name__)


class GeolocationService:
    """
    High-level geolocation service.
    Uses the configured map provider for all operations.
    """
    
    def __init__(self, map_provider: MapProvider):
        """Initialize with a map provider instance"""
        self.provider = map_provider
    
    @classmethod
    async def create(cls, db: AsyncSession) -> "GeolocationService":
        """Factory method to create service with provider from database config"""
        provider = await get_map_provider(db)
        return cls(provider)
    
    async def geocode_address(self, address: str) -> Optional[GeocodingResult]:
        """
        Convert address to coordinates.
        
        Args:
            address: Human-readable address string
            
        Returns:
            GeocodingResult with coordinates or None
        """
        try:
            return await self.provider.geocode(address)
        except Exception as e:
            logger.error(f"Geocoding error: {e}")
            return None
    
    async def reverse_geocode(
        self,
        latitude: float,
        longitude: float
    ) -> Optional[GeocodingResult]:
        """
        Convert coordinates to address.
        
        Args:
            latitude: Latitude coordinate
            longitude: Longitude coordinate
            
        Returns:
            GeocodingResult with address or None
        """
        try:
            return await self.provider.reverse_geocode(latitude, longitude)
        except Exception as e:
            logger.error(f"Reverse geocoding error: {e}")
            return None
    
    async def calculate_distance(
        self,
        origin_lat: float,
        origin_lng: float,
        dest_lat: float,
        dest_lng: float
    ) -> Optional[DistanceResult]:
        """
        Calculate driving distance and time between two points.
        
        Returns:
            DistanceResult with distance_km and duration_minutes
        """
        try:
            return await self.provider.calculate_distance(
                origin_lat, origin_lng, dest_lat, dest_lng
            )
        except Exception as e:
            logger.error(f"Distance calculation error: {e}")
            return None
    
    async def get_route(
        self,
        origin_lat: float,
        origin_lng: float,
        dest_lat: float,
        dest_lng: float,
        waypoints: Optional[List[Tuple[float, float]]] = None
    ) -> Optional[RouteResult]:
        """
        Get driving route between two points.
        
        Returns:
            RouteResult with polyline and distance/duration
        """
        try:
            return await self.provider.get_route(
                origin_lat, origin_lng, dest_lat, dest_lng, waypoints
            )
        except Exception as e:
            logger.error(f"Route calculation error: {e}")
            return None
    
    async def search_places(
        self,
        query: str,
        latitude: Optional[float] = None,
        longitude: Optional[float] = None,
        limit: int = 10
    ) -> List[GeocodingResult]:
        """
        Search for places by text query.
        
        Returns:
            List of GeocodingResult matching the query
        """
        try:
            return await self.provider.search_places(
                query, latitude, longitude, limit
            )
        except Exception as e:
            logger.error(f"Place search error: {e}")
            return []
    
    async def calculate_eta(
        self,
        origin_lat: float,
        origin_lng: float,
        dest_lat: float,
        dest_lng: float
    ) -> Optional[int]:
        """
        Calculate ETA in minutes between two points.
        
        Returns:
            ETA in minutes or None
        """
        try:
            return await self.provider.calculate_eta(
                origin_lat, origin_lng, dest_lat, dest_lng
            )
        except Exception as e:
            logger.error(f"ETA calculation error: {e}")
            return None
    
    def calculate_straight_distance(
        self,
        lat1: float,
        lon1: float,
        lat2: float,
        lon2: float
    ) -> float:
        """
        Calculate straight-line distance (as the crow flies) in km.
        Uses Haversine formula.
        """
        return MapProvider.haversine_distance(lat1, lon1, lat2, lon2)
    
    async def validate_address(self, address: str) -> bool:
        """
        Validate if an address can be geocoded.
        
        Returns:
            True if address is valid and geocodable
        """
        result = await self.geocode_address(address)
        return result is not None
    
    async def test_provider(self) -> Tuple[bool, Optional[str]]:
        """
        Test if the current map provider is working.
        
        Returns:
            Tuple of (success, error_message)
        """
        return await self.provider.test_connection()
