<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * Calculate road distance between two coordinates using Google Maps Distance Matrix API
 * Falls back to Haversine formula if API call fails
 * @param float $lat1 Latitude of first point
 * @param float $lon1 Longitude of first point
 * @param float $lat2 Latitude of second point
 * @param float $lon2 Longitude of second point
 * @param string $apiKey Google Maps API key (optional, will try to get from config)
 * @return float Distance in miles
 */
function calculate_distance($lat1, $lon1, $lat2, $lon2, $apiKey = null) {
    // Validate coordinates
    if (!is_numeric($lat1) || !is_numeric($lon1) || !is_numeric($lat2) || !is_numeric($lon2)) {
        return 0;
    }
    
    // Get API key from config if not provided
    if (empty($apiKey)) {
        if (function_exists('get_instance')) {
            $CI =& get_instance();
            if (isset($CI->config)) {
                $apiKey = $CI->config->item('google_api_key');
            }
        }
    }
    
    // If no API key, fall back to Haversine formula
    if (empty($apiKey)) {
        return calculate_distance_haversine($lat1, $lon1, $lat2, $lon2);
    }
    
    // Use Google Maps Distance Matrix API
    $origin = $lat1 . ',' . $lon1;
    $destination = $lat2 . ',' . $lon2;
    
    $url = 'https://maps.googleapis.com/maps/api/distancematrix/json?' . 
           'origins=' . urlencode($origin) . 
           '&destinations=' . urlencode($destination) . 
           '&units=imperial' . 
           '&key=' . urlencode($apiKey);


    // Make API request with error handling
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlError = curl_error($ch);
    curl_close($ch);
    
    // If API call succeeds, parse the response
    if ($httpCode == 200 && $response && empty($curlError)) {
        $data = json_decode($response, true);
        
        if (isset($data['status']) && $data['status'] == 'OK') {
            if (isset($data['rows'][0]['elements'][0]['status']) && 
                $data['rows'][0]['elements'][0]['status'] == 'OK' &&
                isset($data['rows'][0]['elements'][0]['distance']['value'])) {
                
                $distanceValue = $data['rows'][0]['elements'][0]['distance']['value']; // in meters
                
                // Convert meters to miles
                $distanceMiles = $distanceValue / 1609.34;
                
                return round($distanceMiles, 2);
            }
        }
    }
    
    // Fallback to Haversine if API fails
    return calculate_distance_haversine($lat1, $lon1, $lat2, $lon2);
}

/**
 * Calculate distance between two coordinates using Haversine formula (fallback)
 * @param float $lat1 Latitude of first point
 * @param float $lon1 Longitude of first point
 * @param float $lat2 Latitude of second point
 * @param float $lon2 Longitude of second point
 * @return float Distance in miles
 */
function calculate_distance_haversine($lat1, $lon1, $lat2, $lon2) {
    $earthRadius = 3959; // Earth's radius in miles
    
    $dLat = deg2rad($lat2 - $lat1);
    $dLon = deg2rad($lon2 - $lon1);
    
    $a = sin($dLat / 2) * sin($dLat / 2) +
         cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
         sin($dLon / 2) * sin($dLon / 2);
    
    $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
    $distance = $earthRadius * $c;
    
    return round($distance, 2);
}

/**
 * Get city zones configuration
 * @param string $cityId City ID
 * @param object $common Common model instance
 * @return array Zones configuration
 */
function get_city_zones($cityId, $common) {
    $zonesData = $common->readdatafromcollectionwhereclause('CityZones', 'cityId', '=', $cityId);
    
    if (empty($zonesData)) {
        // Return default zones
        return array(
            'zone1' => array('minDistance' => 0, 'maxDistance' => 12, 'fee' => 3.00, 'minimumOrder' => 25.00),
            'zone2' => array('minDistance' => 12, 'maxDistance' => 20, 'fee' => 6.50, 'minimumOrder' => 25.00, 'storefrontMinimumOrder' => 40.00),
            'zone3' => array('minDistance' => 20, 'maxDistance' => 30, 'fee' => 10.00, 'minimumOrder' => 70.00),
            'zone4' => array('minDistance' => 30, 'maxDistance' => 999999, 'fee' => 15.00, 'minimumOrder' => 125.00)
        );
    }
    
    $zoneData = reset($zonesData);
    return isset($zoneData['zones']) ? $zoneData['zones'] : array();
}

/**
 * Assign zone based on distance
 * @param float $distance Distance in miles
 * @param array $zones Zones configuration
 * @return string Zone key (zone1, zone2, zone3, zone4)
 */
function assign_zone($distance, $zones) {
    foreach ($zones as $zoneKey => $zone) {
        $min = $zone['minDistance'];
        $max = $zone['maxDistance'];
        
        if ($distance >= $min && ($max == 999999 || $distance < $max)) {
            return $zoneKey;
        }
    }
    
    // Default to zone 4 if no match
    return 'zone4';
}

/**
 * Calculate chargeable distance for an order
 * Handles multiple vendors with different fulfillment types:
 * - Cannabus Inventory: HQ → Customer
 * - Storefront Pickup: HQ → Vendor → Customer (for each vendor)
 * - Mixed: Takes the maximum distance among all routes
 * @param array $orderItems Order items with vendor information
 * @param array $city City data with HQ coordinates
 * @param array $customer Customer data with coordinates
 * @param object $common Common model instance
 * @return float Chargeable distance in miles (maximum of all calculated routes)
 */
function calculate_chargeable_distance($orderItems, $city, $customer, $common) {
    $hqLat = isset($city['hqLatitude']) ? $city['hqLatitude'] : null;
    $hqLon = isset($city['hqLongitude']) ? $city['hqLongitude'] : null;
    $customerLat = isset($customer['latitude']) ? $customer['latitude'] : null;
    $customerLon = isset($customer['longitude']) ? $customer['longitude'] : null;
    
    if (!$hqLat || !$hqLon || !$customerLat || !$customerLon) {
        return 0;
    }
    
    $hasCannabusInventory = false;
    $storefrontVendors = array();
    $distances = array(); // Array to store all calculated distances
    
    // Separate order items by fulfillment type
    foreach ($orderItems as $item) {
        if (isset($item['vendor'])) {
            // If vendor is a string (ID), fetch from Users collection
            if (is_string($item['vendor'])) {
                $vendor = $common->readdatadocument('Users', $item['vendor']);
                // Verify it's actually a vendor
                if (empty($vendor) || !isset($vendor['userRole']) || $vendor['userRole'] != 'vendor') {
                    continue;
                }
            } else {
                $vendor = $item['vendor'];
            }
            
            if (isset($vendor['fulfillment_type'])) {
                if ($vendor['fulfillment_type'] == 'cannabus_inventory') {
                    $hasCannabusInventory = true;
                } elseif ($vendor['fulfillment_type'] == 'storefront_pickup') {
                    // Only add unique vendors (by ID) to avoid duplicate calculations
                    $vendorId = isset($vendor['id']) ? $vendor['id'] : (isset($item['vendorId']) ? $item['vendorId'] : null);
                    if ($vendorId && !isset($storefrontVendors[$vendorId])) {
                        $storefrontVendors[$vendorId] = $vendor;
                    }
                }
            } else {
                // Default to Cannabus inventory if fulfillment_type is not set
                $hasCannabusInventory = true;
            }
        }
    }
    
    // Calculate distance for Cannabus inventory items: HQ → Customer
    if ($hasCannabusInventory) {
        $hqToCustomer = calculate_distance($hqLat, $hqLon, $customerLat, $customerLon);
        $distances[] = $hqToCustomer;
    }
    
    // Calculate distance for each storefront vendor: HQ → Vendor → Customer
    // For multiple storefront vendors from different locations, we calculate each route
    // and take the maximum distance (farthest route)
    foreach ($storefrontVendors as $vendorId => $vendor) {
        $vendorLat = isset($vendor['latitude']) ? floatval($vendor['latitude']) : null;
        $vendorLon = isset($vendor['longitude']) ? floatval($vendor['longitude']) : null;
        
        if ($vendorLat && $vendorLon && 
            $vendorLat >= -90 && $vendorLat <= 90 && 
            $vendorLon >= -180 && $vendorLon <= 180) {
            
            // Calculate HQ → Vendor distance
            $hqToVendor = calculate_distance($hqLat, $hqLon, $vendorLat, $vendorLon);
            
            // Calculate Vendor → Customer distance
            $vendorToCustomer = calculate_distance($vendorLat, $vendorLon, $customerLat, $customerLon);
            
            // Total distance for this vendor route: HQ → Vendor → Customer
            $totalDistance = $hqToVendor + $vendorToCustomer;
            $distances[] = $totalDistance;
        }
    }
    
    // Return the maximum distance among all routes
    // This handles:
    // - Multiple storefront vendors from different locations (takes farthest route)
    // - Mixed Cannabus inventory + storefront vendors (takes max of HQ→Customer vs HQ→Vendor→Customer)
    // - Single vendor type (returns that distance)
    if (!empty($distances)) {
        return max($distances);
    }
    
    // No valid items
    return 0;
}

/**
 * Get delivery fee and minimum order for a zone
 * @param string $zoneKey Zone key (zone1, zone2, etc.)
 * @param array $zones Zones configuration
 * @param bool $isStorefront Whether this is a storefront order
 * @return array Array with 'fee' and 'minimumOrder'
 */
function get_zone_pricing($zoneKey, $zones, $isStorefront = false) {
    if (!isset($zones[$zoneKey])) {
        return array('fee' => 0, 'minimumOrder' => 0);
    }
    
    $zone = $zones[$zoneKey];
    $fee = isset($zone['fee']) ? $zone['fee'] : 0;
    $minimumOrder = isset($zone['minimumOrder']) ? $zone['minimumOrder'] : 0;
    
    // Zone 2 storefront special rule
    if ($zoneKey == 'zone2' && $isStorefront && isset($zone['storefrontMinimumOrder'])) {
        $minimumOrder = $zone['storefrontMinimumOrder'];
    }
    
    return array(
        'fee' => $fee,
        'minimumOrder' => $minimumOrder
    );
}

/**
 * Check if driver is within staging distance of city HQ
 * @param float $driverLat Driver latitude
 * @param float $driverLon Driver longitude
 * @param array $city City data with HQ coordinates
 * @param float $maxDistance Maximum staging distance in miles (default 15)
 * @return bool True if driver is within staging distance
 */
function is_driver_within_staging($driverLat, $driverLon, $city, $maxDistance = 15) {
    $hqLat = isset($city['hqLatitude']) ? $city['hqLatitude'] : null;
    $hqLon = isset($city['hqLongitude']) ? $city['hqLongitude'] : null;
    
    if (!$hqLat || !$hqLon) {
        return false;
    }
    
    $distance = calculate_distance($hqLat, $hqLon, $driverLat, $driverLon);
    return $distance <= $maxDistance;
}
