"""
Utility functions for restaurant booking including GPS distance calculation.
"""
from math import radians, cos, sin, asin, sqrt
from datetime import datetime, timedelta


def calculate_distance(lat1, lon1, lat2, lon2):
    """
    Calculate distance between two GPS coordinates using Haversine formula.
    
    Args:
        lat1, lon1: Latitude and longitude of first point (restaurant)
        lat2, lon2: Latitude and longitude of second point (customer)
    
    Returns:
        Distance in kilometers (float)
    """
    # Convert to radians
    lat1, lon1, lat2, lon2 = map(radians, [float(lat1), float(lon1), float(lat2), float(lon2)])
    
    # Haversine formula
    dlat = lat2 - lat1
    dlon = lon2 - lon1
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a))
    
    # Earth's radius in kilometers
    r = 6371
    
    return round(c * r, 2)


def check_delivery_availability(restaurant, customer_lat, customer_lon):
    """
    Check if delivery is available and return distance info.
    
    Args:
        restaurant: Restaurant model instance
        customer_lat: Customer's latitude
        customer_lon: Customer's longitude
    
    Returns:
        dict with:
            - is_deliverable: Boolean indicating if within delivery radius
            - distance_km: Distance in kilometers
            - has_warning: Boolean for far location warning
            - warning_message: Warning message if location is far
    """
    if not restaurant.latitude or not restaurant.longitude:
        return {
            'is_deliverable': False,
            'distance_km': None,
            'has_warning': True,
            'warning_message': 'Restaurant GPS coordinates not set'
        }
    
    distance = calculate_distance(
        restaurant.latitude, restaurant.longitude,
        customer_lat, customer_lon
    )
    
    is_within_radius = distance <= restaurant.delivery_radius_km
    has_warning = distance > restaurant.delivery_radius_km
    
    warning_message = ""
    if has_warning:
        warning_message = f"Your location is {distance} km away. This restaurant delivers within {restaurant.delivery_radius_km} km only."
    
    return {
        'is_deliverable': is_within_radius,
        'distance_km': distance,
        'has_warning': has_warning,
        'warning_message': warning_message
    }


def estimate_delivery_time(distance_km, preparation_time_minutes=30):
    """
    Estimate delivery time based on distance.
    Assumes average speed of 30 km/h for delivery.
    
    Args:
        distance_km: Distance in kilometers
        preparation_time_minutes: Preparation time in minutes (default 30)
    
    Returns:
        datetime object with estimated delivery time
    """
    # Average delivery speed: 30 km/h
    delivery_speed_kmph = 30
    travel_time_minutes = (distance_km / delivery_speed_kmph) * 60
    
    total_minutes = preparation_time_minutes + travel_time_minutes
    estimated_time = datetime.now() + timedelta(minutes=total_minutes)
    
    return estimated_time


def calculate_delivery_charge(distance_km, base_charge=20, per_km_charge=5):
    """
    Calculate delivery charge based on distance.
    
    Args:
        distance_km: Distance in kilometers
        base_charge: Base delivery charge (default ₹20)
        per_km_charge: Charge per km (default ₹5)
    
    Returns:
        Delivery charge amount (float)
    """
    if distance_km <= 2:
        return base_charge  # Free for first 2 km, only base charge
    
    extra_km = distance_km - 2
    return round(base_charge + (extra_km * per_km_charge), 2)


def validate_booking_time(restaurant, booking_date, booking_time):
    """
    Validate if booking time is within restaurant operating hours.
    
    Args:
        restaurant: Restaurant model instance
        booking_date: Booking date
        booking_time: Booking time
    
    Returns:
        dict with is_valid and message
    """
    if not restaurant.opening_time or not restaurant.closing_time:
        return {'is_valid': True, 'message': 'Operating hours not set'}
    
    if booking_time < restaurant.opening_time or booking_time > restaurant.closing_time:
        return {
            'is_valid': False,
            'message': f"Restaurant is open from {restaurant.opening_time.strftime('%I:%M %p')} to {restaurant.closing_time.strftime('%I:%M %p')}"
        }
    
    return {'is_valid': True, 'message': 'Valid booking time'}


def check_table_availability(restaurant, party_size):
    """
    Check if tables are available for the party size.
    
    Args:
        restaurant: Restaurant model instance
        party_size: Number of guests
    
    Returns:
        dict with is_available and tables_needed
    """
    if party_size > restaurant.max_party_size:
        return {
            'is_available': False,
            'tables_needed': 0,
            'message': f'Maximum party size allowed is {restaurant.max_party_size}'
        }
    
    # Assume each table seats 4 people
    tables_per_table = 4
    tables_needed = (party_size + tables_per_table - 1) // tables_per_table  # Ceiling division
    
    if tables_needed > restaurant.tables_available:
        return {
            'is_available': False,
            'tables_needed': tables_needed,
            'message': f'Not enough tables available. Need {tables_needed}, available {restaurant.tables_available}'
        }
    
    return {
        'is_available': True,
        'tables_needed': tables_needed,
        'message': f'{tables_needed} table(s) will be reserved'
    }
