<?php

namespace Opencart\Extension\Alsendo\Services;

/**
 * Resolves client IP to map center (lat/lng) for the Bliska Paczka widget.
 * Uses ip-api.com (no API key; limit 45 req/min). Fallback: country-based defaults.
 */
class GeoIpService
{
    private const API_URL = 'http://ip-api.com/json/%s?fields=status,countryCode,lat,lon';

    /** Default map center and zoom per country (ISO 3166-1 alpha-2). */
    private const COUNTRY_CENTERS = [
        'PL' => ['lat' => 52.0693, 'lng' => 19.4803, 'zoom' => 6],
        'RO' => ['lat' => 45.9432, 'lng' => 24.9668, 'zoom' => 6],
        'CZ' => ['lat' => 49.8175, 'lng' => 15.4730, 'zoom' => 6],
        'SK' => ['lat' => 48.6690, 'lng' => 19.6990, 'zoom' => 6],
        'DE' => ['lat' => 51.1657, 'lng' => 10.4515, 'zoom' => 6],
        'HU' => ['lat' => 47.1625, 'lng' => 19.5033, 'zoom' => 6],
        'UA' => ['lat' => 48.3794, 'lng' => 31.1656, 'zoom' => 6],
        'LT' => ['lat' => 55.1694, 'lng' => 23.8813, 'zoom' => 6],
        'LV' => ['lat' => 56.8796, 'lng' => 24.6032, 'zoom' => 6],
        'EE' => ['lat' => 58.5953, 'lng' => 25.0136, 'zoom' => 6],
        'AT' => ['lat' => 47.5162, 'lng' => 14.5501, 'zoom' => 6],
        'BG' => ['lat' => 42.7339, 'lng' => 25.4858, 'zoom' => 6],
    ];

    /** Default when region is unknown (e.g. Poland as plugin default). */
    private const FALLBACK = ['lat' => 52.0693, 'lng' => 19.4803, 'zoom' => 6];

    /**
     * Get map options (center + zoom) for the given client IP.
     *
     * @param string $clientIp Client IP (e.g. from REMOTE_ADDR or X-Forwarded-For).
     * @param string $pluginRegionCode Plugin region code ('poland'|'czechia'|'romania') for fallback.
     * @return array{center: array{lat: float, lng: float}, zoom: int} Suitable for BPWidget mapOptions.
     */
    public function getMapOptionsFromIp(string $clientIp, string $pluginRegionCode = 'poland'): array
    {
        $clientIp = $this->normalizeIp($clientIp);
        if ($clientIp === '' || $this->isPrivateOrLocal($clientIp)) {
            return $this->getFallbackMapOptions($pluginRegionCode);
        }

        $geo = $this->fetchGeoFromApi($clientIp);
        if ($geo !== null) {
            $countryCode = strtoupper($geo['countryCode'] ?? '');
            $lat = (float) ($geo['lat'] ?? 0);
            $lng = (float) ($geo['lon'] ?? 0);
            if ($countryCode && $lat && $lng) {
                return [
                    'center' => ['lat' => $lat, 'lng' => $lng],
                    'zoom' => 6,
                    'gestureHandling' => true,
                ];
            }
            if ($countryCode && isset(self::COUNTRY_CENTERS[$countryCode])) {
                $c = self::COUNTRY_CENTERS[$countryCode];
                return [
                    'center' => ['lat' => $c['lat'], 'lng' => $c['lng']],
                    'zoom' => $c['zoom'],
                    'gestureHandling' => true,
                ];
            }
        }

        return $this->getFallbackMapOptions($pluginRegionCode);
    }

    private function normalizeIp(string $ip): string
    {
        $ip = trim($ip);
        if (strpos($ip, ',') !== false) {
            $ip = trim(explode(',', $ip)[0]);
        }
        return $ip;
    }

    private function isPrivateOrLocal(string $ip): bool
    {
        return $ip === '127.0.0.1'
            || $ip === '::1'
            || strpos($ip, '192.168.') === 0
            || strpos($ip, '10.') === 0
            || strpos($ip, '172.') === 0;
    }

    /**
     * @return array{status: string, countryCode?: string, lat?: float, lon?: float}|null
     */
    private function fetchGeoFromApi(string $ip): ?array
    {
        $url = sprintf(self::API_URL, rawurlencode($ip));
        $ctx = stream_context_create([
            'http' => [
                'timeout' => 2,
                'ignore_errors' => true,
            ],
        ]);
        $raw = @file_get_contents($url, false, $ctx);
        if ($raw === false) {
            return null;
        }
        $data = json_decode($raw, true);
        if (!is_array($data) || ($data['status'] ?? '') !== 'success') {
            return null;
        }
        return $data;
    }

    private function getFallbackMapOptions(string $pluginRegionCode): array
    {
        $regionToCountry = [
            'poland' => 'PL',
            'romania' => 'RO',
            'czechia' => 'CZ',
        ];
        $countryCode = $regionToCountry[$pluginRegionCode] ?? 'PL';
        $c = self::COUNTRY_CENTERS[$countryCode] ?? self::FALLBACK;
        return [
            'center' => ['lat' => $c['lat'], 'lng' => $c['lng']],
            'zoom' => $c['zoom'],
            'gestureHandling' => true,
        ];
    }
}
