<?php
require_once __DIR__ . '/CountryCodeNormalizer.php';

class GeoIpCountryResolver
{
    /** @var GeoIp2\Database\Reader|null */
    private static $reader = null;

    /** @var array<string, string|null> */
    private static $requestCache = [];

    /** @var string|null */
    private static $lastSource = null;

    /**
     * @param string $ip
     * @return array{code:?string, source:string}
     */
    public static function resolveWithMeta($ip)
    {
        $ip = ClientIpResolver::normalizeIp($ip);
        if ($ip === null) {
            self::$lastSource = 'invalid_ip';
            return ['code' => null, 'source' => 'invalid_ip'];
        }

        if (array_key_exists($ip, self::$requestCache)) {
            self::$lastSource = 'request';
            return ['code' => self::$requestCache[$ip], 'source' => 'request'];
        }

        $cached = GeoIpCountryCache::get($ip);
        if ($cached !== false) {
            self::$requestCache[$ip] = $cached;
            self::$lastSource = 'cache';
            return ['code' => $cached, 'source' => 'cache'];
        }

        $code = self::lookupMmdb($ip);
        GeoIpCountryCache::set($ip, $code);
        self::$requestCache[$ip] = $code;
        self::$lastSource = 'mmdb';
        return ['code' => $code, 'source' => 'mmdb'];
    }

    /**
     * @param string $ip
     * @return string|null ISO 3166-1 alpha-2
     */
    public static function resolve($ip)
    {
        $result = self::resolveWithMeta($ip);
        return $result['code'];
    }

    public static function lastSource()
    {
        return self::$lastSource ?? '';
    }

    public static function isAvailable()
    {
        return self::getReader() !== null;
    }

    /**
     * @param string $ip
     * @return string|null
     */
    private static function lookupMmdb($ip)
    {
        $reader = self::getReader();
        if ($reader === null) {
            return null;
        }
        try {
            $record = $reader->country($ip);
            $isoCode = $record->country->isoCode ?? null;
            $name    = $record->country->name ?? null;

            $code = CountryCodeNormalizer::toAlpha2($isoCode);
            if ($code === null && $name !== null && $name !== '') {
                $code = CountryCodeNormalizer::toAlpha2($name);
            }
            return $code;
        } catch (GeoIp2\Exception\AddressNotFoundException $e) {
            return null;
        } catch (Throwable $e) {
            GeoIpCountryCache::logError('mmdb lookup failed: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * @return GeoIp2\Database\Reader|null
     */
    private static function getReader()
    {
        if (self::$reader !== null) {
            return self::$reader;
        }
        if (!class_exists('GeoIp2\\Database\\Reader')) {
            return null;
        }
        if (!defined('CLOAK_GEOIP_DB_PATH') || !is_file(CLOAK_GEOIP_DB_PATH)) {
            return null;
        }
        try {
            self::$reader = new GeoIp2\Database\Reader(CLOAK_GEOIP_DB_PATH);
            return self::$reader;
        } catch (Throwable $e) {
            GeoIpCountryCache::logError('Reader init failed: ' . $e->getMessage());
            return null;
        }
    }
}
