<?php
declare(strict_types=1);

/**
 * Google AdSense（自動廣告）與網站驗證檔。
 * 設定：fate/data/fate_adsense.json；驗證檔備份：fate/data/adsense_verify/
 * 驗證檔會同步寫入站台根目錄（例：https://fate.novice.tw/google….html）。
 */

if (!function_exists('fate_adsense_path')) {
    function fate_adsense_path(): string
    {
        return dirname(__DIR__) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'fate_adsense.json';
    }
}

if (!function_exists('fate_adsense_verify_data_dir')) {
    function fate_adsense_verify_data_dir(): string
    {
        return dirname(__DIR__) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'adsense_verify';
    }
}

if (!function_exists('fate_adsense_web_root')) {
    function fate_adsense_web_root(): string
    {
        return dirname(__DIR__);
    }
}

if (!function_exists('fate_adsense_default')) {
    /** @return array{enabled:bool,publisher_id:string,skip_admin:bool} */
    function fate_adsense_default(): array
    {
        return [
            'enabled' => false,
            'publisher_id' => '',
            'skip_admin' => true,
        ];
    }
}

if (!function_exists('fate_adsense_read')) {
    /** @return array<string, mixed> */
    function fate_adsense_read(): array
    {
        $p = fate_adsense_path();
        if (!is_file($p)) {
            return [];
        }
        $raw = file_get_contents($p);
        if ($raw === false || trim($raw) === '') {
            return [];
        }
        $j = json_decode($raw, true);

        return is_array($j) ? $j : [];
    }
}

if (!function_exists('fate_adsense_extract_publisher_id')) {
    /** 自 ca-pub-… 或整段 AdSense script 取出發布商 ID */
    function fate_adsense_extract_publisher_id(string $raw): string
    {
        $raw = trim($raw);
        if ($raw === '') {
            return '';
        }
        if (preg_match('/(ca-pub-\d{10,25})/i', $raw, $m) === 1) {
            return strtolower($m[1]);
        }

        return '';
    }
}

if (!function_exists('fate_adsense_normalize')) {
    /**
     * @param array<string, mixed> $merge
     * @return array{enabled:bool,publisher_id:string,skip_admin:bool}
     */
    function fate_adsense_normalize(array $merge): array
    {
        $d = fate_adsense_default();
        if (array_key_exists('enabled', $merge)) {
            $d['enabled'] = !empty($merge['enabled']);
        }
        if (array_key_exists('skip_admin', $merge)) {
            $d['skip_admin'] = !empty($merge['skip_admin']);
        }
        $pub = '';
        if (isset($merge['publisher_id']) && is_string($merge['publisher_id'])) {
            $pub = fate_adsense_extract_publisher_id($merge['publisher_id']);
        }
        $d['publisher_id'] = $pub;

        return $d;
    }
}

if (!function_exists('fate_adsense_persist')) {
    /** @param array{enabled:bool,publisher_id:string,skip_admin:bool} $normalized */
    function fate_adsense_persist(array $normalized): bool
    {
        $dir = dirname(fate_adsense_path());
        if (!is_dir($dir)) {
            if (!@mkdir($dir, 0775, true) && !is_dir($dir)) {
                return false;
            }
        }
        $payload = json_encode($normalized, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
        if ($payload === false) {
            return false;
        }

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

if (!function_exists('fate_adsense_env_publisher_id')) {
    function fate_adsense_env_publisher_id(): string
    {
        $raw = trim((string) (getenv('FATE_ADSENSE_PUBLISHER_ID') ?: ''));

        return fate_adsense_extract_publisher_id($raw);
    }
}

if (!function_exists('fate_adsense_env_enabled_locked')) {
    function fate_adsense_env_enabled_locked(): ?bool
    {
        $v = getenv('FATE_ADSENSE_ENABLED');
        if ($v === false || $v === '') {
            return null;
        }
        $s = strtolower(trim((string) $v));

        return in_array($s, ['1', 'true', 'yes', 'on'], true);
    }
}

if (!function_exists('fate_adsense_resolved')) {
    /**
     * @return array{enabled:bool,publisher_id:string,skip_admin:bool,source:string}
     */
    function fate_adsense_resolved(): array
    {
        $file = fate_adsense_normalize(fate_adsense_read());
        $pub = fate_adsense_env_publisher_id();
        if ($pub !== '') {
            $file['publisher_id'] = $pub;
        }
        $envOn = fate_adsense_env_enabled_locked();
        if ($envOn !== null) {
            $file['enabled'] = $envOn;
        }
        $source = 'file';
        if ($pub !== '' || $envOn !== null) {
            $source = 'env';
        }
        if ($file['publisher_id'] === '') {
            $file['enabled'] = false;
        }

        $file['source'] = $source;

        return $file;
    }
}

if (!function_exists('fate_adsense_is_public_request')) {
    function fate_adsense_is_public_request(): bool
    {
        $script = isset($_SERVER['SCRIPT_NAME']) ? str_replace('\\', '/', (string) $_SERVER['SCRIPT_NAME']) : '';
        if ($script !== '' && preg_match('#/admin(?:/|$)#i', $script) === 1) {
            return false;
        }

        return true;
    }
}

if (!function_exists('fate_adsense_should_render')) {
    function fate_adsense_should_render(): bool
    {
        $c = fate_adsense_resolved();
        if (!$c['enabled'] || $c['publisher_id'] === '') {
            return false;
        }
        if (!empty($c['skip_admin']) && !fate_adsense_is_public_request()) {
            return false;
        }

        return true;
    }
}

if (!function_exists('fate_adsense_head_html')) {
    /** Google AdSense 自動廣告（全站一組 client） */
    function fate_adsense_head_html(): string
    {
        if (!fate_adsense_should_render()) {
            return '';
        }
        $id = fate_adsense_resolved()['publisher_id'];
        if ($id === '') {
            return '';
        }
        $client = htmlspecialchars($id, ENT_QUOTES, 'UTF-8');

        return '<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client='
            . $client
            . '" crossorigin="anonymous"></script>' . "\n";
    }
}

if (!function_exists('fate_adsense_verify_sanitize_filename')) {
    function fate_adsense_verify_sanitize_filename(string $name): ?string
    {
        $name = trim(str_replace('\\', '/', $name));
        $base = basename($name);
        if ($base === '' || $base !== $name || str_contains($base, '..')) {
            return null;
        }
        if (preg_match('/^ads\.txt$/i', $base) === 1) {
            return 'ads.txt';
        }
        if (preg_match('/^google[a-z0-9_-]+\.html$/i', $base) === 1 && strlen($base) <= 128) {
            return $base;
        }

        return null;
    }
}

if (!function_exists('fate_adsense_verify_public_url')) {
    function fate_adsense_verify_public_url(string $filename): string
    {
        if (!function_exists('fate_absolute_url')) {
            require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_member_email.php';
        }

        return fate_absolute_url('/' . $filename);
    }
}

if (!function_exists('fate_adsense_verify_sanitize_content')) {
    function fate_adsense_verify_sanitize_content(string $content, string $filename): ?string
    {
        $content = str_replace("\0", '', $content);
        if (strlen($content) > 65536) {
            return null;
        }
        $content = str_replace(["\r\n", "\r"], "\n", $content);
        $trim = trim($content);
        if ($trim === '') {
            return null;
        }
        if (strcasecmp($filename, 'ads.txt') === 0) {
            if (!preg_match('/^[a-zA-Z0-9=._\-\/\s,]+$/', $trim)) {
                return null;
            }

            return $trim . "\n";
        }
        if (preg_match('/^google[a-z0-9_-]+\.html$/i', $filename) !== 1) {
            return null;
        }
        if (strlen($trim) > 4096) {
            return null;
        }

        return $trim;
    }
}

if (!function_exists('fate_adsense_verify_list')) {
    /**
     * @return list<array{name:string,uploaded_at:string,public_url:string,web_ok:bool,data_ok:bool}>
     */
    function fate_adsense_verify_list(): array
    {
        $dir = fate_adsense_verify_data_dir();
        if (!is_dir($dir)) {
            return [];
        }
        $webRoot = fate_adsense_web_root();
        $out = [];
        $dh = @opendir($dir);
        if ($dh === false) {
            return [];
        }
        while (($entry = readdir($dh)) !== false) {
            if ($entry === '.' || $entry === '..') {
                continue;
            }
            $safe = fate_adsense_verify_sanitize_filename($entry);
            if ($safe === null) {
                continue;
            }
            $dataPath = $dir . DIRECTORY_SEPARATOR . $safe;
            if (!is_file($dataPath)) {
                continue;
            }
            $webPath = $webRoot . DIRECTORY_SEPARATOR . $safe;
            $out[] = [
                'name' => $safe,
                'uploaded_at' => date('Y-m-d H:i:s', (int) filemtime($dataPath)),
                'public_url' => fate_adsense_verify_public_url($safe),
                'web_ok' => is_file($webPath),
                'data_ok' => true,
            ];
        }
        closedir($dh);
        usort($out, static fn (array $a, array $b): int => strcmp($b['name'], $a['name']));

        return $out;
    }
}

if (!function_exists('fate_adsense_verify_upload')) {
    /**
     * @return array{ok:bool, error?:string, name?:string, public_url?:string}
     */
    function fate_adsense_verify_upload(string $filename, string $content): array
    {
        $safe = fate_adsense_verify_sanitize_filename($filename);
        if ($safe === null) {
            return ['ok' => false, 'error' => '檔名須為 Google 提供的 google….html 或 ads.txt'];
        }
        $body = fate_adsense_verify_sanitize_content($content, $safe);
        if ($body === null) {
            return ['ok' => false, 'error' => '檔案內容無效或過大'];
        }

        $dataDir = fate_adsense_verify_data_dir();
        if (!is_dir($dataDir)) {
            if (!@mkdir($dataDir, 0775, true) && !is_dir($dataDir)) {
                return ['ok' => false, 'error' => '無法建立 data/adsense_verify 目錄'];
            }
        }

        $dataPath = $dataDir . DIRECTORY_SEPARATOR . $safe;
        $webPath = fate_adsense_web_root() . DIRECTORY_SEPARATOR . $safe;

        if (file_put_contents($dataPath, $body, LOCK_EX) === false) {
            return ['ok' => false, 'error' => '無法寫入備份檔'];
        }
        if (file_put_contents($webPath, $body, LOCK_EX) === false) {
            @unlink($dataPath);

            return ['ok' => false, 'error' => '無法寫入站台根目錄（請檢查 ' . basename(fate_adsense_web_root()) . ' 寫入權限）'];
        }
        @chmod($dataPath, 0644);
        @chmod($webPath, 0644);

        return [
            'ok' => true,
            'name' => $safe,
            'public_url' => fate_adsense_verify_public_url($safe),
        ];
    }
}

if (!function_exists('fate_adsense_verify_delete')) {
    /** @return array{ok:bool, error?:string} */
    function fate_adsense_verify_delete(string $filename): array
    {
        $safe = fate_adsense_verify_sanitize_filename($filename);
        if ($safe === null) {
            return ['ok' => false, 'error' => '檔名無效'];
        }
        $dataPath = fate_adsense_verify_data_dir() . DIRECTORY_SEPARATOR . $safe;
        $webPath = fate_adsense_web_root() . DIRECTORY_SEPARATOR . $safe;
        $ok = true;
        if (is_file($dataPath) && !@unlink($dataPath)) {
            $ok = false;
        }
        if (is_file($webPath) && !@unlink($webPath)) {
            $ok = false;
        }
        if (!$ok) {
            return ['ok' => false, 'error' => '刪除失敗（請檢查檔案權限）'];
        }

        return ['ok' => true];
    }
}

if (!function_exists('fate_adsense_admin_links')) {
    /**
     * @return list<array{label:string,url:string,hint?:string}>
     */
    function fate_adsense_admin_links(): array
    {
        return [
            [
                'label' => '申請 Google AdSense',
                'url' => 'https://www.google.com/adsense/start/',
                'hint' => '需自有網域與可瀏覽內容；審核通過後取得 ca-pub- 發布商 ID',
            ],
            [
                'label' => 'AdSense 主控台',
                'url' => 'https://www.google.com/adsense/',
                'hint' => '管理網站、收益、政策與付款',
            ],
            [
                'label' => '新增／管理網站',
                'url' => 'https://www.google.com/adsense/new/u/0/pub-settings/sites',
                'hint' => '將本站網址加入 AdSense 並完成驗證',
            ],
            [
                'label' => '說明：驗證網站擁有權',
                'url' => 'https://support.google.com/adsense/answer/10532',
                'hint' => 'HTML 檔案上傳至網站根目錄',
            ],
            [
                'label' => '說明：自動廣告（Auto ads）',
                'url' => 'https://support.google.com/adsense/answer/9262830',
                'hint' => '全站只需一組 adsbygoogle.js?client=ca-pub-…',
            ],
            [
                'label' => '說明：ads.txt',
                'url' => 'https://support.google.com/adsense/answer/7532444',
                'hint' => '若 AdSense 要求可上傳 ads.txt',
            ],
        ];
    }
}

if (!function_exists('fate_adsense_save_admin_post')) {
    /**
     * @param array<string, mixed> $post
     * @return array{ok:bool, error?:string}
     */
    function fate_adsense_save_admin_post(array $post): array
    {
        if (fate_adsense_env_enabled_locked() !== null || fate_adsense_env_publisher_id() !== '') {
            return ['ok' => false, 'error' => '發布商 ID 或啟用狀態已由環境變數鎖定，請改伺服器設定或移除 FATE_ADSENSE_* 後再儲存。'];
        }

        $cur = fate_adsense_normalize(fate_adsense_read());
        $cur['enabled'] = !empty($post['adsense_enabled']);
        $cur['skip_admin'] = !isset($post['adsense_skip_admin']) || !empty($post['adsense_skip_admin']);
        $pubIn = isset($post['publisher_id']) && is_string($post['publisher_id']) ? $post['publisher_id'] : '';
        if (trim($pubIn) !== '') {
            $extracted = fate_adsense_extract_publisher_id($pubIn);
            if ($extracted === '') {
                return ['ok' => false, 'error' => '無法辨識發布商 ID，請貼上 ca-pub- 開頭的 ID 或官方提供的 script 片段。'];
            }
            $cur['publisher_id'] = $extracted;
        } elseif (empty($post['adsense_enabled'])) {
            // 關閉時允許不變更 publisher_id
        } elseif ($cur['publisher_id'] === '') {
            return ['ok' => false, 'error' => '啟用自動廣告前請先填寫發布商 ID（ca-pub-…）。'];
        }

        if (!fate_adsense_persist(fate_adsense_normalize($cur))) {
            return ['ok' => false, 'error' => '無法寫入 fate/data/fate_adsense.json'];
        }

        return ['ok' => true];
    }
}
