<?php
if (!defined('ABSPATH')) {
    exit;
}

class BotBlocker_Geo {
    public static function init() {
        add_action('bot_blocker_geo_resolve', [__CLASS__, 'resolve_async']);
    }

    public static function prime($ip) {
        $empty = self::empty_result();
        $settings = BotBlocker_Settings::get();

        if (empty($settings['geolocation_enabled']) || !self::is_public_ip($ip)) {
            return $empty;
        }

        $cache_key = self::cache_key($ip);
        $cached = get_transient($cache_key);
        if (is_array($cached)) {
            return wp_parse_args($cached, $empty);
        }

        $pending_key = self::pending_key($ip);
        if (!get_transient($pending_key)) {
            set_transient($pending_key, 1, 10 * MINUTE_IN_SECONDS);
            wp_schedule_single_event(time() + 5, 'bot_blocker_geo_resolve', [$ip]);
        }

        return $empty;
    }

    public static function resolve_async($ip) {
        if (!self::is_public_ip($ip)) {
            return;
        }

        $data = self::remote_lookup($ip);
        $ttl = empty($data['country']) && empty($data['city']) && empty($data['latitude']) && empty($data['longitude'])
            ? 15 * MINUTE_IN_SECONDS
            : 7 * DAY_IN_SECONDS;

        set_transient(self::cache_key($ip), $data, $ttl);
        delete_transient(self::pending_key($ip));

        // Backfill any recently stored events for this IP that have no geo data yet
        if (!empty($data['latitude']) && !empty($data['longitude'])) {
            global $wpdb;
            $ip_hash = BotBlocker_Storage::hash_ip($ip);
            $wpdb->query($wpdb->prepare(
                'UPDATE ' . BotBlocker_Storage::table() . '
                 SET country = %s, region = %s, city = %s, latitude = %f, longitude = %f, geo_source = %s
                 WHERE ip_hash = %s AND latitude IS NULL
                   AND occurred_at >= %s',
                $data['country'],
                $data['region'],
                $data['city'],
                (float) $data['latitude'],
                (float) $data['longitude'],
                $data['geo_source'],
                $ip_hash,
                gmdate('Y-m-d H:i:s', time() - 30 * MINUTE_IN_SECONDS)
            ));
        }
    }

    private static function remote_lookup($ip) {
        $data = self::empty_result();
        $response = wp_remote_get(
            'https://ipwho.is/' . rawurlencode($ip),
            [
                'timeout'     => 2,
                'redirection' => 1,
                'user-agent'  => 'BotBlocker/' . BOT_BLOCKER_VERSION,
            ]
        );

        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
            return $data;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        if (empty($body['success'])) {
            return $data;
        }

        return [
            'country'    => sanitize_text_field($body['country'] ?? ''),
            'region'     => sanitize_text_field($body['region'] ?? ''),
            'city'       => sanitize_text_field($body['city'] ?? ''),
            'latitude'   => isset($body['latitude']) ? (float) $body['latitude'] : null,
            'longitude'  => isset($body['longitude']) ? (float) $body['longitude'] : null,
            'geo_source' => 'ipwho.is',
        ];
    }

    private static function empty_result() {
        return [
            'country'    => '',
            'region'     => '',
            'city'       => '',
            'latitude'   => null,
            'longitude'  => null,
            'geo_source' => '',
        ];
    }

    private static function cache_key($ip) {
        return 'bot_blocker_geo_' . md5((string) $ip);
    }

    private static function pending_key($ip) {
        return 'bot_blocker_geo_pending_' . md5((string) $ip);
    }

    private static function is_public_ip($ip) {
        return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
    }
}
