<?php
namespace Core;

/**
 * Resolves a visitor's country (and derived currency) from their IP address
 * via the ipinfo.io Lite API (api.ipinfo.io/lite/{ip}) — a free tier that
 * returns just ip/asn/country/continent, no city/region/timezone. The API
 * token is configured by the platform admin under Admin > System Settings
 * (stored as the 'ipinfo_token' platform setting).
 */
class GeoIP {
    /** Best-effort real client IP, accounting for a reverse proxy/CDN in front of the app. */
    public static function clientIp(): string {
        foreach (['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'] as $key) {
            if (!empty($_SERVER[$key])) {
                // X-Forwarded-For may be a comma-separated chain; the client is the first entry.
                $candidate = trim(explode(',', $_SERVER[$key])[0]);
                if (filter_var($candidate, FILTER_VALIDATE_IP)) {
                    return $candidate;
                }
            }
        }
        return '0.0.0.0';
    }

    private static function isPrivateOrReserved(string $ip): bool {
        // Loopback/LAN/reserved ranges (localhost dev, internal networks) can't be geolocated.
        return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
    }

    /**
     * Look up an IP's country and its derived currency.
     * Returns null on any failure (no token configured, network error, private IP, etc.)
     * — callers should fall back to the platform's default currency in that case.
     */
    public static function lookup(string $ip): ?array {
        if ($ip === '0.0.0.0' || self::isPrivateOrReserved($ip)) {
            return null;
        }

        $token = getPlatformSetting('ipinfo_token', '');
        if ($token === '') {
            return null;
        }

        $url = 'https://api.ipinfo.io/lite/' . rawurlencode($ip) . '?token=' . rawurlencode($token);

        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 4,
            CURLOPT_CONNECTTIMEOUT => 3,
            CURLOPT_HTTPHEADER     => ['Accept: application/json'],
        ]);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response === false || $httpCode !== 200) {
            return null;
        }

        $data = json_decode($response, true);
        // Lite API uses 'country_code' for the ISO-2 code ('country' is the full name).
        $country = strtoupper((string)($data['country_code'] ?? ''));
        if ($country === '') {
            return null;
        }

        return [
            'ip'       => $ip,
            'country'  => $country,
            'currency' => CountryCurrency::forCountry($country),
        ];
    }
}
