<?php
/**
 * SmartThink LMS - GeoLocation Class
 * 
 * Handles geo-location detection, regional access control, and compliance
 */

class GeoLocation {
    private $db;
    private $apiKey;
    private $cache;
    
    public function __construct() {
        $this->db = Database::getInstance();
        $this->apiKey = $this->getGeoApiKey();
        $this->cache = new Cache();
    }
    
    /**
     * Detect user location from IP address
     */
    public function detectLocation($ip = null) {
        $ip = $ip ?: getClientIP();
        
        // Check cache first
        $cacheKey = "geo_location_{$ip}";
        $cached = $this->cache->get($cacheKey);
        
        if ($cached) {
            return $cached;
        }
        
        // Get location data
        $locationData = $this->fetchLocationData($ip);
        
        if ($locationData) {
            // Cache for 24 hours
            $this->cache->set($cacheKey, $locationData, 86400);
            
            // Log location detection
            $this->logLocationDetection($ip, $locationData);
            
            return $locationData;
        }
        
        return $this->getDefaultLocation();
    }
    
    /**
     * Get user's assigned region
     */
    public function getUserRegion($userId = null) {
        $userId = $userId ?: getCurrentUserId();
        
        if (!$userId) {
            return $this->detectRegionFromIP();
        }
        
        $user = $this->db->get("SELECT region, country FROM users WHERE id = ?", [$userId]);
        
        if ($user && $user['region']) {
            return $user['region'];
        }
        
        // Detect and update user region
        $detectedRegion = $this->detectRegionFromIP();
        
        if ($user) {
            $this->db->update('users', ['region' => $detectedRegion], 'id = ?', [$userId]);
        }
        
        return $detectedRegion;
    }
    
    /**
     * Detect region from IP address
     */
    public function detectRegionFromIP($ip = null) {
        $location = $this->detectLocation($ip);
        return $this->mapCountryToRegion($location['country_code'] ?? 'US');
    }
    
    /**
     * Check if user can access resource in their region
     */
    public function canAccessResource($resourceRegion, $userRegion = null) {
        $userRegion = $userRegion ?: $this->getUserRegion();
        
        // Global resources are accessible to everyone
        if ($resourceRegion === 'global') {
            return true;
        }
        
        // Check if regions match
        if ($resourceRegion === $userRegion) {
            return true;
        }
        
        // Check for regional access override (admin roles can access all)
        $currentRole = $_SESSION['role'] ?? null;
        if ($currentRole && in_array($currentRole, ['super_admin', 'admin'], true)) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Enforce regional access control
     */
    public function enforceRegionalAccess($resourceRegion, $redirectUrl = null) {
        if (!$this->canAccessResource($resourceRegion)) {
            $userRegion = $this->getUserRegion();
            
            // Log access attempt
            $this->logAccessViolation($resourceRegion, $userRegion);
            
            // Show friendly message and redirect
            $message = "This content is not available in your region ({$userRegion}). " .
                      "You have been redirected to your regional content.";
            
            setFlashMessage('warning', $message);
            
            if ($redirectUrl) {
                redirect($redirectUrl);
            } else {
                redirect("../website/index.php?region={$userRegion}");
            }
        }
    }
    
    /**
     * Get regional pricing for product
     */
    public function getRegionalPricing($basePrice, $baseCurrency = 'USD', $region = null) {
        $region = $region ?: $this->getUserRegion();
        
        // Get exchange rates
        $rates = $this->getExchangeRates();
        
        // Regional pricing rules
        $regionalMultipliers = [
            'usa' => 1.0,        // Base price
            'europe' => 0.85,    // EUR conversion
            'africa' => 0.0024   // NGN conversion (approximate)
        ];
        
        $regionalCurrencies = [
            'usa' => 'USD',
            'europe' => 'EUR',
            'africa' => 'NGN'
        ];
        
        $multiplier = $regionalMultipliers[$region] ?? 1.0;
        $currency = $regionalCurrencies[$region] ?? $baseCurrency;
        
        $regionalPrice = $basePrice * $multiplier;
        
        return [
            'price' => $regionalPrice,
            'currency' => $currency,
            'formatted' => $this->formatPrice($regionalPrice, $currency),
            'region' => $region
        ];
    }
    
    /**
     * Get regional content and settings
     */
    public function getRegionalContent($contentType, $region = null) {
        $region = $region ?: $this->getUserRegion();
        
        $content = $this->db->get(
            "SELECT * FROM regional_content 
             WHERE content_type = ? AND region = ? AND is_active = 1",
            [$contentType, $region]
        );
        
        if (!$content) {
            // Fallback to global content
            $content = $this->db->get(
                "SELECT * FROM regional_content 
                 WHERE content_type = ? AND region = 'global' AND is_active = 1",
                [$contentType]
            );
        }
        
        return $content;
    }
    
    /**
     * Get compliance settings for region
     */
    public function getComplianceSettings($region = null) {
        $region = $region ?: $this->getUserRegion();
        
        $settings = [
            'usa' => [
                'data_retention_days' => 365,
                'cookie_consent_required' => true,
                'age_verification_required' => false,
                'tax_rate' => 0.08, // 8% sales tax
                'privacy_policy_version' => 'us_v1.0',
                'gdpr_compliant' => false
            ],
            'europe' => [
                'data_retention_days' => 730,
                'cookie_consent_required' => true,
                'age_verification_required' => true,
                'tax_rate' => 0.21, // 21% VAT
                'privacy_policy_version' => 'gdpr_v1.0',
                'gdpr_compliant' => true
            ],
            'africa' => [
                'data_retention_days' => 180,
                'cookie_consent_required' => false,
                'age_verification_required' => false,
                'tax_rate' => 0.05, // 5% VAT
                'privacy_policy_version' => 'africa_v1.0',
                'gdpr_compliant' => false
            ]
        ];
        
        return $settings[$region] ?? $settings['usa'];
    }
    
    /**
     * Validate regional compliance
     */
    public function validateCompliance($action, $data = []) {
        $region = $this->getUserRegion();
        $settings = $this->getComplianceSettings($region);
        
        switch ($action) {
            case 'user_registration':
                return $this->validateRegistrationCompliance($region, $data, $settings);
            
            case 'data_processing':
                return $this->validateDataProcessingCompliance($region, $data, $settings);
            
            case 'payment_processing':
                return $this->validatePaymentCompliance($region, $data, $settings);
            
            default:
                return true;
        }
    }
    
    /**
     * Get location statistics
     */
    public function getLocationStats($period = '30 days') {
        $sql = "SELECT region, country, city, COUNT(*) as user_count,
                       AVG(latitude) as avg_latitude, AVG(longitude) as avg_longitude
                FROM (
                    SELECT region, country, city,
                           CASE 
                               WHEN country IN ('US', 'CA', 'MX') THEN 40.7128
                               WHEN country IN ('GB', 'DE', 'FR', 'IT', 'ES') THEN 48.8566
                               WHEN country IN ('NG', 'ZA', 'KE', 'GH') THEN 6.5244
                               ELSE 0 
                           END as latitude,
                           CASE 
                               WHEN country IN ('US', 'CA', 'MX') THEN -74.0060
                               WHEN country IN ('GB', 'DE', 'FR', 'IT', 'ES') THEN 2.3522
                               WHEN country IN ('NG', 'ZA', 'KE', 'GH') THEN -3.3792
                               ELSE 0 
                           END as longitude
                    FROM users 
                    WHERE status = 'active' 
                    AND created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
                ) as location_data
                GROUP BY region, country, city
                ORDER BY user_count DESC";
        
        return $this->db->getAll($sql);
    }
    
    /**
     * Fetch location data from API
     */
    private function fetchLocationData($ip) {
        // Try multiple services in order of preference
        
        // 1. ipapi.co (most accurate)
        $data = $this->fetchFromIpapi($ip);
        if ($data) return $data;
        
        // 2. ip-api.com (free tier)
        $data = $this->fetchFromIpApiCom($ip);
        if ($data) return $data;
        
        // 3. ipinfo.io (free tier)
        $data = $this->fetchFromIpinfo($ip);
        if ($data) return $data;
        
        return null;
    }
    
    /**
     * Fetch from ipapi.co
     */
    private function fetchFromIpapi($ip) {
        $url = "https://ipapi.co/{$ip}/json/";
        
        $response = $this->makeApiCall($url);
        
        if ($response && isset($response['country_code'])) {
            return [
                'ip' => $ip,
                'country_code' => $response['country_code'],
                'country_name' => $response['country_name'],
                'region' => $response['region'],
                'city' => $response['city'],
                'latitude' => $response['latitude'],
                'longitude' => $response['longitude'],
                'timezone' => $response['timezone'],
                'provider' => 'ipapi'
            ];
        }
        
        return null;
    }
    
    /**
     * Fetch from ip-api.com
     */
    private function fetchFromIpApiCom($ip) {
        $url = "http://ip-api.com/json/{$ip}";
        
        $response = $this->makeApiCall($url);
        
        if ($response && $response['status'] === 'success') {
            return [
                'ip' => $ip,
                'country_code' => $response['countryCode'],
                'country_name' => $response['country'],
                'region' => $response['regionName'],
                'city' => $response['city'],
                'latitude' => $response['lat'],
                'longitude' => $response['lon'],
                'timezone' => $response['timezone'],
                'provider' => 'ip-api'
            ];
        }
        
        return null;
    }
    
    /**
     * Fetch from ipinfo.io
     */
    private function fetchFromIpinfo($ip) {
        $url = "https://ipinfo.io/{$ip}/json";
        
        $response = $this->makeApiCall($url);
        
        if ($response && isset($response['country'])) {
            $location = explode(',', $response['loc'] ?? '');
            
            return [
                'ip' => $ip,
                'country_code' => $response['country'],
                'country_name' => $response['country'],
                'region' => $response['region'],
                'city' => $response['city'],
                'latitude' => $location[0] ?? null,
                'longitude' => $location[1] ?? null,
                'timezone' => $response['timezone'],
                'provider' => 'ipinfo'
            ];
        }
        
        return null;
    }
    
    /**
     * Make API call
     */
    private function makeApiCall($url) {
        $context = stream_context_create([
            'http' => [
                'timeout' => 5,
                'user_agent' => 'SmartThink LMS/1.0'
            ]
        ]);
        
        $response = @file_get_contents($url, false, $context);
        
        if ($response) {
            return json_decode($response, true);
        }
        
        return null;
    }
    
    /**
     * Map country to region
     */
    private function mapCountryToRegion($countryCode) {
        $mapping = [
            // USA Region
            'US' => 'usa', 'CA' => 'usa', 'MX' => 'usa',
            
            // Europe Region
            'GB' => 'europe', 'DE' => 'europe', 'FR' => 'europe',
            'IT' => 'europe', 'ES' => 'europe', 'NL' => 'europe',
            'BE' => 'europe', 'AT' => 'europe', 'CH' => 'europe',
            'SE' => 'europe', 'NO' => 'europe', 'DK' => 'europe',
            'FI' => 'europe', 'PL' => 'europe', 'CZ' => 'europe',
            'HU' => 'europe', 'GR' => 'europe', 'PT' => 'europe',
            'IE' => 'europe',
            
            // Africa Region
            'NG' => 'africa', 'ZA' => 'africa', 'KE' => 'africa',
            'GH' => 'africa', 'EG' => 'africa', 'MA' => 'africa',
            'TN' => 'africa', 'DZ' => 'africa', 'ET' => 'africa',
            'TZ' => 'africa', 'UG' => 'africa', 'ZW' => 'africa',
            'BW' => 'africa', 'MW' => 'africa', 'ZM' => 'africa',
            'AO' => 'africa', 'MZ' => 'africa'
        ];
        
        return $mapping[$countryCode] ?? 'usa';
    }
    
    /**
     * Get default location
     */
    private function getDefaultLocation() {
        return [
            'ip' => getClientIP(),
            'country_code' => 'US',
            'country_name' => 'United States',
            'region' => 'California',
            'city' => 'San Francisco',
            'latitude' => 37.7749,
            'longitude' => -122.4194,
            'timezone' => 'America/Los_Angeles',
            'provider' => 'default'
        ];
    }
    
    /**
     * Get exchange rates
     */
    private function getExchangeRates() {
        $cacheKey = 'exchange_rates';
        $rates = $this->cache->get($cacheKey);
        
        if (!$rates) {
            // In production, use a real exchange rate API
            $rates = [
                'USD' => 1.0,
                'EUR' => 0.85,
                'NGN' => 410.0
            ];
            
            // Cache for 1 hour
            $this->cache->set($cacheKey, $rates, 3600);
        }
        
        return $rates;
    }
    
    /**
     * Format price
     */
    private function formatPrice($price, $currency) {
        $symbols = [
            'USD' => '$',
            'EUR' => 'â‚¬',
            'NGN' => 'â‚¦'
        ];
        
        $symbol = $symbols[$currency] ?? $currency;
        
        if ($currency === 'NGN') {
            return $symbol . number_format($price, 2);
        } else {
            return $symbol . number_format($price, 2);
        }
    }
    
    /**
     * Validate registration compliance
     */
    private function validateRegistrationCompliance($region, $data, $settings) {
        // GDPR compliance for Europe
        if ($settings['gdpr_compliant']) {
            // Check if consent was given
            if (!isset($data['gdpr_consent']) || !$data['gdpr_consent']) {
                throw new Exception("GDPR consent is required for registration in your region");
            }
            
            // Check age verification
            if ($settings['age_verification_required']) {
                if (!isset($data['date_of_birth']) || !$this->isAdult($data['date_of_birth'])) {
                    throw new Exception("Age verification is required for registration in your region");
                }
            }
        }
        
        return true;
    }
    
    /**
     * Validate data processing compliance
     */
    private function validateDataProcessingCompliance($region, $data, $settings) {
        // GDPR compliance checks
        if ($settings['gdpr_compliant']) {
            // Log data processing activity
            $this->logDataProcessing($region, $data);
            
            // Check data retention
            if (isset($data['created_at'])) {
                $createdDate = new DateTime($data['created_at']);
                $now = new DateTime();
                $daysDiff = $now->diff($createdDate)->days;
                
                if ($daysDiff > $settings['data_retention_days']) {
                    throw new Exception("Data retention period exceeded for your region");
                }
            }
        }
        
        return true;
    }
    
    /**
     * Validate payment compliance
     */
    private function validatePaymentCompliance($region, $data, $settings) {
        // Check if payment method is available in region
        $availableMethods = $this->getRegionalPaymentMethods($region);
        
        if (!in_array($data['payment_method'], $availableMethods)) {
            throw new Exception("Payment method not available in your region");
        }
        
        // Apply regional tax
        $data['tax_amount'] = $data['amount'] * $settings['tax_rate'];
        
        return $data;
    }
    
    /**
     * Get regional payment methods
     */
    private function getRegionalPaymentMethods($region) {
        $methods = [
            'usa' => ['stripe', 'paypal', 'bank_transfer'],
            'europe' => ['stripe', 'paypal', 'bank_transfer', 'sepa'],
            'africa' => ['stripe', 'paypal', 'mobile_money', 'bank_transfer']
        ];
        
        return $methods[$region] ?? ['stripe', 'paypal'];
    }
    
    /**
     * Check if user is adult
     */
    private function isAdult($dateOfBirth) {
        $dob = new DateTime($dateOfBirth);
        $now = new DateTime();
        $age = $now->diff($dob)->y;
        
        return $age >= 18;
    }
    
    /**
     * Get geo API key
     */
    private function getGeoApiKey() {
        return $this->db->get(
            "SELECT setting_value FROM system_settings WHERE setting_key = 'geo_api_key'"
        )['setting_value'] ?? null;
    }
    
    /**
     * Log location detection
     */
    private function logLocationDetection($ip, $location) {
        $this->db->insert('location_logs', [
            'ip_address' => $ip,
            'country_code' => $location['country_code'],
            'country_name' => $location['country_name'],
            'region' => $location['region'],
            'city' => $location['city'],
            'latitude' => $location['latitude'],
            'longitude' => $location['longitude'],
            'provider' => $location['provider'],
            'detected_at' => formatDate()
        ]);
    }
    
    /**
     * Log access violation
     */
    private function logAccessViolation($resourceRegion, $userRegion) {
        $this->db->insert('access_violations', [
            'user_id' => getCurrentUserId(),
            'ip_address' => getClientIP(),
            'resource_region' => $resourceRegion,
            'user_region' => $userRegion,
            'user_agent' => getUserAgent(),
            'created_at' => formatDate()
        ]);
    }
    
    /**
     * Log data processing
     */
    private function logDataProcessing($region, $data) {
        $this->db->insert('data_processing_logs', [
            'user_id' => getCurrentUserId(),
            'region' => $region,
            'data_type' => $data['type'] ?? 'unknown',
            'action' => $data['action'] ?? 'unknown',
            'created_at' => formatDate()
        ]);
    }
}

/**
 * Simple Cache Class
 */
class Cache {
    private $cacheDir;
    
    public function __construct() {
        $this->cacheDir = __DIR__ . '/../cache/';
        if (!is_dir($this->cacheDir)) {
            mkdir($this->cacheDir, 0755, true);
        }
    }
    
    public function get($key) {
        $file = $this->cacheDir . md5($key) . '.cache';
        
        if (file_exists($file)) {
            $data = unserialize(file_get_contents($file));
            
            if ($data['expires'] > time()) {
                return $data['value'];
            } else {
                unlink($file);
            }
        }
        
        return null;
    }
    
    public function set($key, $value, $ttl = 3600) {
        $file = $this->cacheDir . md5($key) . '.cache';
        $data = [
            'value' => $value,
            'expires' => time() + $ttl
        ];
        
        file_put_contents($file, serialize($data));
    }
    
    public function delete($key) {
        $file = $this->cacheDir . md5($key) . '.cache';
        if (file_exists($file)) {
            unlink($file);
        }
    }
    
    public function clear() {
        $files = glob($this->cacheDir . '*.cache');
        foreach ($files as $file) {
            unlink($file);
        }
    }
}
?>
