<?php

namespace App\Services\QrCode;

use App\Services\QrCode\Contracts\GeoLocator;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;

/**
 * Resolves IP geo data via the free, keyless ip-api.com lookup.
 *
 * Since scan IPs may already be truncated to a /24 (or /48) before this
 * runs (see `IpAnonymizer`), the resolved location is only ever accurate
 * to city-ish/regional granularity by design, not a precise address.
 */
class HttpGeoLocator implements GeoLocator
{
    /**
     * How long a successful lookup is cached per-IP.
     */
    private const CACHE_TTL_HOURS = 24;

    /**
     * Request timeout, in seconds. Kept short since this runs inside a
     * queued job that should not stall behind a slow/unreachable API.
     */
    private const TIMEOUT_SECONDS = 2;

    public function locate(string $ipAddress): ?array
    {
        return Cache::remember(
            "qr-code:geo:{$ipAddress}",
            now()->addHours(self::CACHE_TTL_HOURS),
            fn (): ?array => $this->fetch($ipAddress),
        );
    }

    /**
     * @return array{country_code: ?string, country: ?string, region: ?string, city: ?string, latitude: ?string, longitude: ?string}|null
     */
    private function fetch(string $ipAddress): ?array
    {
        try {
            $response = Http::timeout(self::TIMEOUT_SECONDS)
                ->get("http://ip-api.com/json/{$ipAddress}");

            if (! $response->successful()) {
                return null;
            }

            $data = $response->json();

            if (! is_array($data) || ($data['status'] ?? null) !== 'success') {
                return null;
            }

            return [
                'country_code' => $data['countryCode'] ?? null,
                'country' => $data['country'] ?? null,
                'region' => $data['regionName'] ?? null,
                'city' => $data['city'] ?? null,
                'latitude' => isset($data['lat']) ? (string) $data['lat'] : null,
                'longitude' => isset($data['lon']) ? (string) $data['lon'] : null,
            ];
        } catch (Throwable $e) {
            // Fail soft: analytics should degrade, never break enrichment.
            Log::warning('QR code geo lookup failed', [
                'ip' => $ipAddress,
                'exception' => $e->getMessage(),
            ]);

            return null;
        }
    }
}
