<?php
declare(strict_types=1);

/**
 * FATE 站台 API 金鑰：登錄表、讀寫 fate/data/fate_secrets.json、環境變數優先。
 * 後台「網站與模版」頁可設定；勿將 fate_secrets.json 提交版本庫。
 */

if (!function_exists('fate_api_keys_registry')) {
    /**
     * 新增金鑰：在此登錄 id，後台表單與 fate_api_key() 即會支援。
     *
     * @return array<string, array{
     *   label:string,
     *   hint:string,
     *   group:string,
     *   env:string,
     *   env_legacy?:string,
     *   apply_urls?:list<array{label:string,url:string}>
     * }>
     */
    function fate_api_keys_registry(): array
    {
        $googleCredential = 'https://console.cloud.google.com/apis/credentials';
        $googleBilling = 'https://console.cloud.google.com/billing';
        $spaBase = defined('FATE_SPA_API_BASE')
            ? rtrim((string) FATE_SPA_API_BASE, '/')
            : rtrim((string) (getenv('FATE_SPA_API_BASE') ?: 'https://spa.novice.tw'), '/');

        return [
            'google_geocoding' => [
                'label' => 'Google Geocoding API',
                'hint' => '後台／伺服器地址轉座標。必須另外啟用「Geocoding API」（僅開 Maps JavaScript 不夠）並開通計費；金鑰建議限制伺服器「IP 位址」。可與 google_maps_js 共用同一把金鑰。',
                'group' => '地圖與定位',
                'env' => 'FATE_API_KEY_GOOGLE_GEOCODING',
                'apply_urls' => [
                    ['label' => 'Google Cloud 主控台', 'url' => 'https://console.cloud.google.com/'],
                    ['label' => '建立／管理 API 金鑰', 'url' => $googleCredential],
                    ['label' => '啟用 Geocoding API', 'url' => 'https://console.cloud.google.com/apis/library/geocoding-backend.googleapis.com'],
                    ['label' => '帳單與計費設定', 'url' => $googleBilling],
                ],
            ],
            'google_maps_js' => [
                'label' => 'Google Maps JavaScript API',
                'hint' => '僅供未來前台 JS 地圖元件；店家詳情頁已改用免費 OpenStreetMap，不需此金鑰。可與 Geocoding 共用。',
                'group' => '地圖與定位',
                'env' => 'FATE_API_KEY_GOOGLE_MAPS_JS',
                'env_legacy' => 'GOOGLE_API_KEY',
                'apply_urls' => [
                    ['label' => 'Google Cloud 主控台', 'url' => 'https://console.cloud.google.com/'],
                    ['label' => '建立／管理 API 金鑰', 'url' => $googleCredential],
                    ['label' => '啟用 Maps JavaScript API', 'url' => 'https://console.cloud.google.com/apis/library/maps-javascript-api.googleapis.com'],
                    ['label' => '帳單與計費設定', 'url' => $googleBilling],
                ],
            ],
            'spa_store_public' => [
                'label' => '新手村店家公開 API',
                'hint' => '同步時使用的 X-Store-API-Key；請與「新手村 API」頁產生並申請通過的同一把金鑰一致。不是 Brevo 金鑰。',
                'group' => '外部整合',
                'env' => 'FATE_API_KEY_SPA_STORE_PUBLIC',
                'env_legacy' => 'FATE_SPA_STORE_API_KEY',
                'apply_urls' => [
                    ['label' => 'FATE — 產生金鑰與申請說明', 'url' => 'spa_api_key.php'],
                    ['label' => '新手村 — API 金鑰申請', 'url' => $spaBase . '/s_api_key_apply.php'],
                    ['label' => '新手村 — 合作網站列表', 'url' => $spaBase . '/store_public_partners.php'],
                ],
            ],
            'brevo_email' => [
                'label' => 'Brevo 寄信 API',
                'hint' => '會員驗證信等 Transactional Email（優先於主機 mail()）。請填 xkeysib- 開頭的 v3 API Key；與「新手村店家公開 API」不同欄位。',
                'group' => '郵件',
                'env' => 'FATE_API_KEY_BREVO_EMAIL',
                'env_legacy' => 'FATE_BREVO_API_KEY',
                'apply_urls' => [
                    ['label' => 'Brevo 註冊／登入', 'url' => 'https://www.brevo.com/'],
                    ['label' => 'SMTP 與 API 金鑰（主控台）', 'url' => 'https://app.brevo.com/settings/keys/api'],
                    ['label' => 'Transactional Email 說明', 'url' => 'https://developers.brevo.com/docs/send-a-transactional-email'],
                ],
            ],
            'smtp_password' => [
                'label' => 'SMTP 寄信密碼',
                'hint' => 'SMTP 發信（PHPMailer）使用的密碼或應用程式密碼；主機、連接埠、帳號於後台「驗證信與發信」頁設定。Gmail 等請使用「應用程式密碼」而非登入密碼。',
                'group' => '郵件',
                'env' => 'FATE_API_KEY_SMTP_PASSWORD',
                'env_legacy' => 'FATE_SMTP_PASSWORD',
                'apply_urls' => [
                    ['label' => '驗證信與發信設定', 'url' => 'email_settings.php'],
                    ['label' => 'Gmail 應用程式密碼', 'url' => 'https://myaccount.google.com/apppasswords'],
                ],
            ],
            'imap_password' => [
                'label' => '反向驗證 IMAP 密碼',
                'hint' => '反向 Email 驗證（會員寄信到驗證信箱）所用的 IMAP 收信密碼。主機、信箱、驗證方式於後台「驗證信與發信」頁設定。對外 25 埠被封、無法主動寄信時使用。',
                'group' => '郵件',
                'env' => 'FATE_API_KEY_IMAP_PASSWORD',
                'env_legacy' => 'FATE_IMAP_PASSWORD',
                'apply_urls' => [
                    ['label' => '驗證信與發信設定', 'url' => 'email_settings.php'],
                ],
            ],
        ];
    }
}

if (!function_exists('fate_api_keys_path')) {
    function fate_api_keys_path(): string
    {
        $fromEnv = getenv('FATE_SECRETS_FILE');
        if (is_string($fromEnv) && trim($fromEnv) !== '') {
            return trim($fromEnv);
        }

        return dirname(__DIR__) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'fate_secrets.json';
    }
}

if (!function_exists('fate_api_keys_read_file')) {
    /** @return array<string, string> */
    function fate_api_keys_read_file(): array
    {
        $p = fate_api_keys_path();
        if (!is_file($p)) {
            return [];
        }
        $raw = file_get_contents($p);
        if ($raw === false || trim($raw) === '') {
            return [];
        }
        $j = json_decode($raw, true);
        if (!is_array($j)) {
            return [];
        }
        $out = [];
        foreach (fate_api_keys_registry() as $id => $_) {
            if (isset($j[$id]) && is_string($j[$id])) {
                $v = trim($j[$id]);
                if ($v !== '') {
                    $out[$id] = $v;
                }
            }
        }

        return $out;
    }
}

if (!function_exists('fate_api_keys_persist')) {
    /** @param array<string, string> $secrets */
    function fate_api_keys_persist(array $secrets): bool
    {
        $dir = dirname(fate_api_keys_path());
        if (!is_dir($dir)) {
            if (!@mkdir($dir, 0775, true) && !is_dir($dir)) {
                return false;
            }
        }
        $payload = json_encode($secrets, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        if ($payload === false) {
            return false;
        }

        return file_put_contents(fate_api_keys_path(), $payload . "\n", LOCK_EX) !== false;
    }
}

if (!function_exists('fate_api_key_from_env')) {
    function fate_api_key_from_env(string $id): string
    {
        $reg = fate_api_keys_registry()[$id] ?? null;
        if ($reg === null) {
            return '';
        }
        $names = [];
        if (!empty($reg['env_legacy'])) {
            $names[] = (string) $reg['env_legacy'];
        }
        if (!empty($reg['env'])) {
            $names[] = (string) $reg['env'];
        }
        foreach ($names as $name) {
            $v = getenv($name);
            if (is_string($v) && trim($v) !== '') {
                return trim($v);
            }
        }

        return '';
    }
}

if (!function_exists('fate_api_key')) {
    /** 取得金鑰：環境變數 > fate_secrets.json > 空字串 */
    function fate_api_key(string $id): string
    {
        if (!preg_match('/^[a-z][a-z0-9_]{0,63}$/', $id)) {
            return '';
        }
        $env = fate_api_key_from_env($id);
        if ($env !== '') {
            return trim($env);
        }
        $file = fate_api_keys_read_file();

        return trim($file[$id] ?? '');
    }
}

if (!function_exists('fate_api_key_mask')) {
    function fate_api_key_mask(string $value): string
    {
        $value = trim($value);
        $len = function_exists('mb_strlen') ? mb_strlen($value, 'UTF-8') : strlen($value);
        if ($len < 8) {
            return str_repeat('•', min(8, max(4, $len)));
        }
        $tail = function_exists('mb_substr') ? mb_substr($value, -4, 4, 'UTF-8') : substr($value, -4);

        return '••••' . $tail;
    }
}

if (!function_exists('fate_api_key_looks_like_brevo')) {
    function fate_api_key_looks_like_brevo(string $value): bool
    {
        $v = trim($value);

        return $v !== '' && preg_match('/^x(keysib|smtpsib)-/i', $v) === 1;
    }
}

if (!function_exists('fate_api_keys_misplacement_warnings')) {
    /**
     * 偵測 Brevo 金鑰填錯欄位（常見：貼在 spa_store_public）。
     *
     * @return list<array{field:string, label:string, message:string}>
     */
    function fate_api_keys_misplacement_warnings(): array
    {
        $registry = fate_api_keys_registry();
        $file = fate_api_keys_read_file();
        $warnings = [];
        $brevoInFile = trim($file['brevo_email'] ?? '');

        foreach ($file as $id => $value) {
            if ($id === 'brevo_email' || !is_string($value)) {
                continue;
            }
            if (!fate_api_key_looks_like_brevo($value)) {
                continue;
            }
            $label = $registry[$id]['label'] ?? $id;
            if ($brevoInFile === '') {
                $warnings[] = [
                    'field' => $id,
                    'label' => $label,
                    'message' => '偵測到 Brevo 格式金鑰（xkeysib-／xsmtpsib-）在「' . $label . '」欄位，但「Brevo 寄信 API」尚未設定。請至網站與模版 → 郵件 → Brevo 寄信 API 貼上金鑰，並還原「' . $label . '」為正確值。',
                ];
            } elseif ($id !== 'brevo_email') {
                $warnings[] = [
                    'field' => $id,
                    'label' => $label,
                    'message' => '「' . $label . '」內容像 Brevo 金鑰，可能填錯欄位；該欄應為新手村店家 API Key。',
                ];
            }
        }

        return $warnings;
    }
}

if (!function_exists('fate_api_key_admin_status')) {
    /**
     * @return array{state:string, label:string, masked:string}
     */
    function fate_api_key_admin_status(string $id): array
    {
        $env = fate_api_key_from_env($id);
        if ($env !== '') {
            return [
                'state' => 'env',
                'label' => '已由環境變數設定（後台檔案不覆寫）',
                'masked' => fate_api_key_mask($env),
            ];
        }
        $file = fate_api_keys_read_file();
        if (isset($file[$id]) && $file[$id] !== '') {
            return [
                'state' => 'file',
                'label' => '已儲存於本機設定檔',
                'masked' => fate_api_key_mask($file[$id]),
            ];
        }

        return [
            'state' => 'unset',
            'label' => '尚未設定',
            'masked' => '',
        ];
    }
}

if (!function_exists('fate_api_keys_grouped_registry')) {
    /** @return array<string, array<string, array{label:string,hint:string,group:string,env:string,env_legacy?:string}>> */
    function fate_api_keys_grouped_registry(): array
    {
        $groups = [];
        foreach (fate_api_keys_registry() as $id => $meta) {
            $g = $meta['group'];
            if (!isset($groups[$g])) {
                $groups[$g] = [];
            }
            $groups[$g][$id] = $meta;
        }

        return $groups;
    }
}

if (!function_exists('fate_api_keys_save_admin_post')) {
    /**
     * @param array<string, mixed> $values  POST api_key_value[id]
     * @param array<string, mixed> $clear   POST api_key_clear[id]=1
     * @return array{ok:bool, error:?string}
     */
    function fate_api_keys_save_admin_post(array $values, array $clear): array
    {
        $registry = fate_api_keys_registry();
        $secrets = fate_api_keys_read_file();
        $changed = false;

        foreach ($registry as $id => $_) {
            if (fate_api_key_from_env($id) !== '') {
                continue;
            }
            if (!empty($clear[$id])) {
                if (isset($secrets[$id])) {
                    unset($secrets[$id]);
                    $changed = true;
                }
                continue;
            }
            if (!isset($values[$id]) || !is_string($values[$id])) {
                continue;
            }
            $v = trim($values[$id]);
            if ($v === '') {
                continue;
            }
            if (strlen($v) > 512) {
                return ['ok' => false, 'error' => '金鑰過長：' . $id];
            }
            if ($secrets[$id] ?? '' !== $v) {
                $secrets[$id] = $v;
                $changed = true;
            }
        }

        if (!$changed) {
            return ['ok' => true, 'error' => null];
        }
        if (!fate_api_keys_persist($secrets)) {
            return ['ok' => false, 'error' => '無法寫入 fate/data/fate_secrets.json（請檢查目錄權限）。'];
        }

        return ['ok' => true, 'error' => null];
    }
}
