<?php
declare(strict_types=1);

/**
 * Google Geocoding API（伺服器端）。
 * 金鑰：google_geocoding → 若空則沿用 google_maps_js（常為同一把 GCP 金鑰）。
 */

require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_api_keys.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'osm_nominatim.php';

if (!function_exists('fate_google_geocode_api_key')) {
    function fate_google_geocode_api_key(): string
    {
        $k = fate_api_key('google_geocoding');
        if ($k !== '') {
            return $k;
        }

        return fate_api_key('google_maps_js');
    }
}

if (!function_exists('fate_google_geocode_configured')) {
    function fate_google_geocode_configured(): bool
    {
        return fate_google_geocode_api_key() !== '';
    }
}

if (!function_exists('fate_google_geocode_precision')) {
    function fate_google_geocode_precision(string $locationType, bool $partialMatch): string
    {
        if ($partialMatch) {
            return 'district';
        }
        $t = strtoupper(trim($locationType));
        if (in_array($t, ['ROOFTOP', 'RANGE_INTERPOLATED'], true)) {
            return 'address';
        }
        if (in_array($t, ['GEOMETRIC_CENTER', 'APPROXIMATE'], true)) {
            return 'district';
        }

        return 'address';
    }
}

if (!function_exists('fate_google_pick_best_result')) {
    /**
     * @param list<array<string,mixed>> $results
     * @return array<string,mixed>|null
     */
    function fate_google_pick_best_result(array $results): ?array
    {
        $rank = ['ROOFTOP' => 0, 'RANGE_INTERPOLATED' => 1, 'GEOMETRIC_CENTER' => 2, 'APPROXIMATE' => 3];
        $best = null;
        $bestRank = 99;
        foreach ($results as $hit) {
            if (!is_array($hit)) {
                continue;
            }
            $t = strtoupper((string) ($hit['geometry']['location_type'] ?? 'APPROXIMATE'));
            $r = $rank[$t] ?? 4;
            if ($r < $bestRank) {
                $bestRank = $r;
                $best = $hit;
            }
        }

        return $best;
    }
}

if (!function_exists('fate_google_geocode_friendly_error')) {
    function fate_google_geocode_friendly_error(string $raw): string
    {
        $raw = trim($raw);
        if ($raw === '') {
            return '請確認金鑰與 Geocoding API 已啟用';
        }
        if (stripos($raw, 'not activated') !== false || stripos($raw, 'enable this API') !== false) {
            return '此 GCP 專案尚未啟用 Geocoding API，請至後台「網站與模版」金鑰區的「啟用 Geocoding API」連結開啟（不是只開 Maps JavaScript）';
        }
        if (stripos($raw, 'referer') !== false || stripos($raw, 'referrer') !== false) {
            return '金鑰設為「HTTP 網域」限制，無法給伺服器用；請改「IP 位址」限制或另建伺服器用金鑰';
        }
        if (strlen($raw) > 200) {
            return mb_substr($raw, 0, 200, 'UTF-8') . '…';
        }

        return $raw;
    }
}

if (!function_exists('fate_google_geocode_parse_response')) {
    /**
     * @param array<string,mixed> $data
     * @return array{ok:bool,lat?:float,lng?:float,display_name?:string,google_map_location?:string,error?:string,precision?:string,provider?:string}
     */
    function fate_google_geocode_parse_response(array $data, string $fallbackAddress): array
    {
        $status = strtoupper(trim((string) ($data['status'] ?? '')));
        if ($status === 'REQUEST_DENIED') {
            $msg = fate_google_geocode_friendly_error((string) ($data['error_message'] ?? ''));

            return ['ok' => false, 'error' => 'Google：' . $msg];
        }
        if ($status === 'OVER_QUERY_LIMIT') {
            return ['ok' => false, 'error' => 'Google：已超過配額或請求過於頻繁'];
        }
        if ($status === 'INVALID_REQUEST') {
            return ['ok' => false, 'error' => 'Google：請求無效'];
        }
        if ($status !== 'OK' || !is_array($data['results'] ?? null) || $data['results'] === []) {
            return ['ok' => false, 'error' => 'Google：找不到符合的地址（' . ($status !== '' ? $status : 'ZERO_RESULTS') . '）'];
        }

        $hit = fate_google_pick_best_result($data['results']);
        if ($hit === null) {
            return ['ok' => false, 'error' => 'Google：無有效結果'];
        }

        $loc = $hit['geometry']['location'] ?? null;
        if (!is_array($loc)) {
            return ['ok' => false, 'error' => 'Google：缺少座標'];
        }

        $lat = isset($loc['lat']) ? (float) $loc['lat'] : 0.0;
        $lng = isset($loc['lng']) ? (float) $loc['lng'] : 0.0;
        if (!is_finite($lat) || !is_finite($lng) || abs($lat) > 90 || abs($lng) > 180) {
            return ['ok' => false, 'error' => 'Google：座標無效'];
        }

        $partial = !empty($hit['partial_match']);
        $locType = (string) ($hit['geometry']['location_type'] ?? '');
        $precision = fate_google_geocode_precision($locType, $partial);
        $display = trim((string) ($hit['formatted_address'] ?? $fallbackAddress));
        $gml = number_format($lat, 6, '.', '') . ',' . number_format($lng, 6, '.', '');

        $out = [
            'ok' => true,
            'lat' => $lat,
            'lng' => $lng,
            'display_name' => $display,
            'google_map_location' => $gml,
            'precision' => $precision,
            'provider' => 'google',
        ];
        if ($precision === 'district') {
            $out['display_name'] = $display . '（Google 僅定位至近似區域，請核對或手動微調 GPS）';
        }

        return $out;
    }
}

if (!function_exists('fate_google_geocode_http')) {
    /**
     * @param array<string, string> $queryParams
     * @return array{ok:bool,lat?:float,lng?:float,display_name?:string,google_map_location?:string,error?:string,precision?:string,provider?:string}|array{ok:false,skip:true,error?:string}
     */
    function fate_google_geocode_http(array $queryParams, string $fallbackAddress): array
    {
        $apiKey = fate_google_geocode_api_key();
        if ($apiKey === '') {
            return ['ok' => false, 'skip' => true, 'error' => '未設定 Google API 金鑰（請於後台填 google_geocoding 或 google_maps_js）'];
        }

        $queryParams['key'] = $apiKey;
        $queryParams['region'] = $queryParams['region'] ?? 'tw';
        $queryParams['language'] = $queryParams['language'] ?? 'zh-TW';

        $url = 'https://maps.googleapis.com/maps/api/geocode/json?' . http_build_query($queryParams);
        $headers = [
            'Accept: application/json',
            'User-Agent: ' . fate_osm_nominatim_user_agent(),
        ];

        $res = fate_osm_http_get($url, $headers);
        if (empty($res['ok'])) {
            return ['ok' => false, 'error' => '無法連線 Google Geocoding：' . ($res['error'] ?? 'unknown')];
        }

        $http = (int) ($res['http'] ?? 0);
        if ($http !== 200) {
            return ['ok' => false, 'error' => 'Google Geocoding HTTP ' . $http];
        }

        $data = json_decode((string) ($res['body'] ?? ''), true);
        if (!is_array($data)) {
            return ['ok' => false, 'error' => 'Google 回應格式錯誤'];
        }

        return fate_google_geocode_parse_response($data, $fallbackAddress);
    }
}

if (!function_exists('fate_google_components_string')) {
    /**
     * @param array<string, string> $parts
     */
    function fate_google_components_string(array $parts): string
    {
        $segments = [];
        foreach ($parts as $k => $v) {
            $v = trim($v);
            if ($v === '') {
                continue;
            }
            $segments[] = $k . ':' . $v;
        }

        return implode('|', $segments);
    }
}

if (!function_exists('fate_google_geocode_components')) {
    /**
     * @return array{ok:bool,lat?:float,lng?:float,display_name?:string,google_map_location?:string,error?:string,precision?:string,provider?:string}|array{ok:false,skip:true,error?:string}
     */
    function fate_google_geocode_components(string $city, string $district, string $street, string $zip = ''): array
    {
        $city = fate_normalize_tw_address_part($city);
        $district = fate_normalize_tw_address_part($district);
        $street = fate_normalize_tw_address_part($street);
        $zip = preg_replace('/\D/', '', trim($zip));

        $fallback = fate_build_geocode_query($city, $district, $street);
        $attempts = [];

        $base = ['country' => 'TW'];
        if ($zip !== '') {
            $attempts[] = array_merge($base, ['postal_code' => $zip]);
        }
        if ($street !== '' && $district !== '' && $city !== '') {
            $attempts[] = array_merge($base, [
                'administrative_area' => $city,
                'locality' => $district,
                'route' => $street,
            ]);
            $attempts[] = array_merge($base, [
                'administrative_area' => $city,
                'sublocality' => $district,
                'route' => $street,
            ]);
            if ($zip !== '') {
                $attempts[] = array_merge($base, [
                    'postal_code' => $zip,
                    'route' => $street,
                ]);
            }
        }
        if ($district !== '' && $city !== '') {
            $attempts[] = array_merge($base, [
                'administrative_area' => $city,
                'locality' => $district,
            ]);
        }

        $seen = [];
        $lastError = 'Google：找不到符合的座標';
        foreach ($attempts as $parts) {
            $comp = fate_google_components_string($parts);
            if ($comp === '' || isset($seen[$comp])) {
                continue;
            }
            $seen[$comp] = true;
            $result = fate_google_geocode_http(['components' => $comp], $fallback);
            if (!empty($result['skip'])) {
                return $result;
            }
            if (!empty($result['ok'])) {
                return $result;
            }
            $lastError = (string) ($result['error'] ?? $lastError);
        }

        return ['ok' => false, 'error' => $lastError];
    }
}

if (!function_exists('fate_google_geocode_request')) {
    /**
     * @return array{ok:bool,lat?:float,lng?:float,display_name?:string,google_map_location?:string,error?:string,precision?:string,provider?:string}|array{ok:false,skip:true,error?:string}
     */
    function fate_google_geocode_request(string $address): array
    {
        $address = trim($address);
        if ($address === '') {
            return ['ok' => false, 'error' => '地址為空'];
        }

        return fate_google_geocode_http(['address' => $address], $address);
    }
}

if (!function_exists('fate_google_geocode_address')) {
    /**
     * @return array{ok:bool,lat?:float,lng?:float,display_name?:string,google_map_location?:string,error?:string,precision?:string,provider?:string}|array{ok:false,skip:true,error?:string}
     */
    function fate_google_geocode_address(string $city, string $district, string $street, string $zip = ''): array
    {
        $city = fate_normalize_tw_address_part($city);
        $district = fate_normalize_tw_address_part($district);
        $street = fate_normalize_tw_address_part($street);

        if ($city === '' && $district === '' && $street === '') {
            return ['ok' => false, 'error' => '請輸入較完整的地址'];
        }

        if (!fate_google_geocode_configured()) {
            return ['ok' => false, 'skip' => true, 'error' => '未設定 Google API 金鑰'];
        }

        $comp = fate_google_geocode_components($city, $district, $street, $zip);
        if (!empty($comp['ok'])) {
            return $comp;
        }
        if (!empty($comp['skip'])) {
            return $comp;
        }
        $lastError = (string) ($comp['error'] ?? '');

        $queries = [];
        if ($street !== '' && $city !== '' && $district !== '') {
            $queries[] = fate_build_geocode_query($city, $district, $street);
            $queries[] = '台灣' . $city . $district . $street;
            $queries[] = $city . $district . $street;
        }

        $seen = [];
        foreach ($queries as $q) {
            $q = trim($q);
            if ($q === '' || isset($seen[$q])) {
                continue;
            }
            $seen[$q] = true;
            $result = fate_google_geocode_request($q);
            if (!empty($result['ok'])) {
                return $result;
            }
            $lastError = (string) ($result['error'] ?? $lastError);
        }

        return ['ok' => false, 'error' => $lastError !== '' ? $lastError : 'Google：找不到符合的座標'];
    }
}

if (!function_exists('fate_google_geocode_search')) {
    /**
     * @return array{ok:bool,lat?:float,lng?:float,display_name?:string,google_map_location?:string,error?:string,precision?:string,provider?:string}|array{ok:false,skip:true,error?:string}
     */
    function fate_google_geocode_search(string $query): array
    {
        $query = trim($query);
        if (mb_strlen($query, 'UTF-8') < 3) {
            return ['ok' => false, 'error' => '請輸入較完整的地址'];
        }

        if (!fate_google_geocode_configured()) {
            return ['ok' => false, 'skip' => true, 'error' => '未設定 Google API 金鑰'];
        }

        return fate_google_geocode_request($query);
    }
}
