<?php

declare(strict_types=1);

/**
 * FATE 瀏覽統計 — 底層資料為 fate_visit_log（逐筆紀錄）；此檔提供 page_key 規則與後台／前台聚合介面。
 */

require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_visit_log.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_store_listing.php';

function fate_page_counter_normalize_key(string $raw): ?string
{
    return fate_visit_log_normalize_page_key($raw);
}

function fate_page_stats_city_slug(string $city): string
{
    $city = trim($city);
    if ($city === '') {
        return '';
    }
    if (preg_match('/^[a-z0-9_-]{1,48}$/', $city) === 1) {
        return $city;
    }

    return substr(hash('sha256', $city), 0, 12);
}

/**
 * 首頁（index.php）計數鍵：區分排序、縣市篩選、分頁。
 */
function fate_page_stats_build_home_key(int $page, string $cityFilter, string $sort, string $storeSearchQ, string $listingFilter = 'fixed'): string
{
    require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_store_listing.php';
    $parts = ['home'];
    $listing = fate_store_listing_normalize_tab($listingFilter);
    if ($listing !== FATE_STORE_LISTING_FIXED) {
        $parts[] = 'listing_' . $listing;
    }
    if ($sort !== 'rating') {
        $parts[] = $sort;
    }
    $city = trim($cityFilter);
    if ($city !== '') {
        $parts[] = 'c_' . fate_page_stats_city_slug($city);
    }
    $q = trim($storeSearchQ);
    if ($q !== '') {
        $parts[] = 'q_' . substr(hash('sha256', $q), 0, 12);
    }
    if ($page > 1) {
        $parts[] = 'p' . $page;
    }

    $raw = implode('.', $parts);
    $norm = fate_visit_log_normalize_page_key($raw);

    return $norm ?? 'home';
}

/**
 * 本週週一日期 Y-m-d（伺服器預設時區，ISO 週一至週日為一週）。
 */
function fate_stats_week_start_monday(?int $unixTs = null): string
{
    $tz = new DateTimeZone(date_default_timezone_get());
    $dt = new DateTimeImmutable('@' . ($unixTs ?? time()));
    $dt = $dt->setTimezone($tz)->setTime(0, 0, 0);
    $dow = (int) $dt->format('N');

    return $dt->modify('-' . ($dow - 1) . ' days')->format('Y-m-d');
}

/**
 * 本週熱門店家（依該週詳情頁瀏覽次數；定點／非定點各自獨立排行）。
 *
 * @return list<array<string,mixed>>
 */
function fate_store_weekly_top_fetch(
    PDO $pdo,
    string $weekStartYmd,
    int $limit = 10,
    string $listingKind = FATE_STORE_LISTING_FIXED
): array {
    return fate_visit_log_weekly_top_stores($pdo, $weekStartYmd, $limit, $listingKind);
}

/**
 * 寫入瀏覽紀錄並回傳該 page_key 累計次數。
 *
 * @return int|null 累計筆數；無 PDO／鍵無效／查詢失敗為 null
 */
function fate_page_counter_increment(?PDO $pdo, string $pageKey, ?int $memberId = null, ?string $pageTitle = null): ?int
{
    if (!$pdo instanceof PDO) {
        return null;
    }

    fate_visit_log_append($pdo, $pageKey, $memberId, $pageTitle);

    return fate_visit_log_count_by_page($pdo, $pageKey);
}

/**
 * 在線人數的判定時間窗（分鐘）；可用環境變數 FATE_ONLINE_WINDOW_MIN 覆寫（1–60，預設 5）。
 */
function fate_page_stats_online_window_minutes(): int
{
    $env = getenv('FATE_ONLINE_WINDOW_MIN');
    if ($env !== false && is_numeric($env)) {
        $m = (int) $env;
        if ($m >= 1 && $m <= 60) {
            return $m;
        }
    }

    return 5;
}

/**
 * 計算目前在線人數（近 N 分鐘不重複 client_ip 數，含當前訪客）。失敗回 null。
 */
function fate_page_stats_online_count_compute(?PDO $pdo): ?int
{
    if (!$pdo instanceof PDO) {
        return null;
    }
    try {
        if (!fate_visit_log_table_exists($pdo)) {
            return null;
        }

        return fate_visit_log_distinct_ip_in_window($pdo, fate_page_stats_online_window_minutes());
    } catch (Throwable) {
        return null;
    }
}

/**
 * 在線人數快取（供 site_footer.php 於頁腳渲染時取用，免在每個頁面手動傳遞）。
 */
function fate_page_stats_online_count_cache(?int $value = null, bool $write = false): ?int
{
    static $cached = null;
    if ($write) {
        $cached = $value;
    }

    return $cached;
}

/** 取得本次請求已計算的在線人數（未計算過則回 null）。 */
function fate_page_stats_online_count(): ?int
{
    return fate_page_stats_online_count_cache();
}

/**
 * 前台 index.php / store.php 共用：寫入瀏覽並產生頁腳變數（同一套 fate_visit_log + 舊表 fallback）。
 *
 * @return array{fatePageHitCount: ?int, fatePageStatsDbOk: bool, fateOnlineCount: ?int}
 */
function fate_page_stats_for_footer(
    ?PDO $pdo,
    string $pageKey,
    ?int $memberId = null,
    ?string $pageTitle = null
): array {
    $statsDbOk = $pdo instanceof PDO;
    $hitCount = null;
    if ($pdo instanceof PDO && trim($pageKey) !== '') {
        $hitCount = fate_page_counter_increment($pdo, $pageKey, $memberId, $pageTitle);
        if ($hitCount === null) {
            $statsDbOk = fate_visit_log_table_exists($pdo);
        }
    }

    $online = fate_page_stats_online_count_compute($pdo);
    fate_page_stats_online_count_cache($online, true);

    return [
        'fatePageHitCount' => $hitCount,
        'fatePageStatsDbOk' => $statsDbOk,
        'fateOnlineCount' => $online,
    ];
}

/** @param array{id:int,username:string,display_name:string,avatar_rel:string}|null $fatePublicMember */
function fate_page_stats_member_id(?array $fatePublicMember): ?int
{
    if ($fatePublicMember === null || !isset($fatePublicMember['id'])) {
        return null;
    }
    $mid = (int) $fatePublicMember['id'];

    return $mid > 0 ? $mid : null;
}

/** @return array{pdo: ?PDO, dbError: ?string} */
function fate_frontend_db_bootstrap(): array
{
    require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_db.php';
    $dbError = null;
    $pdo = null;
    try {
        $pdo = fate_bootstrap_pdo($dbError);
    } catch (Throwable $e) {
        $dbError = $e->getMessage();
        $pdo = null;
    }

    return ['pdo' => $pdo, 'dbError' => $dbError];
}

/** @return list<array{page_key:string,page_title:?string,hit_count:int,updated_at:string}> */
function fate_page_hit_stats_fetch_all(PDO $pdo): array
{
    fate_visit_log_ensure_table($pdo);
    fate_visit_log_ensure_page_title_column($pdo);
    $st = $pdo->query(
        'SELECT t.page_key, t.hit_count, t.updated_at,
                (
                    SELECT v.page_title
                    FROM fate_visit_log v
                    WHERE v.page_key = t.page_key
                      AND v.page_title IS NOT NULL
                      AND TRIM(v.page_title) <> \'\'
                    ORDER BY v.viewed_at DESC
                    LIMIT 1
                ) AS page_title
         FROM (
             SELECT page_key,
                    COUNT(*) AS hit_count,
                    DATE_FORMAT(MAX(viewed_at), \'%Y-%m-%d %H:%i:%s\') AS updated_at
             FROM fate_visit_log
             GROUP BY page_key
         ) t
         ORDER BY t.hit_count DESC, t.page_key ASC'
    );
    if (!$st) {
        return [];
    }
    $out = [];
    while ($row = $st->fetch(PDO::FETCH_ASSOC)) {
        if (!is_array($row)) {
            continue;
        }
        $pk = isset($row['page_key']) ? (string) $row['page_key'] : '';
        if ($pk === '') {
            continue;
        }
        $pt = isset($row['page_title']) && is_string($row['page_title']) ? trim($row['page_title']) : '';
        $out[] = [
            'page_key' => $pk,
            'page_title' => $pt !== '' ? $pt : null,
            'hit_count' => isset($row['hit_count']) ? (int) $row['hit_count'] : 0,
            'updated_at' => isset($row['updated_at']) ? (string) $row['updated_at'] : '',
        ];
    }

    return $out;
}

function fate_page_hit_stats_total_hits(PDO $pdo): int
{
    fate_visit_log_ensure_table($pdo);
    $n = $pdo->query('SELECT COUNT(*) FROM fate_visit_log')->fetchColumn();

    return (int) $n;
}

/** @return list<array{spa_store_id:int,hit_count:int,updated_at:string,store_name:?string}> */
function fate_store_hit_stats_fetch_top(PDO $pdo, int $limit = 80): array
{
    fate_visit_log_ensure_table($pdo);
    $limit = max(1, min(500, $limit));
    $sql = 'SELECT CAST(SUBSTRING(v.page_key FROM 7) AS UNSIGNED) AS spa_store_id,
                   COUNT(*) AS hit_count,
                   DATE_FORMAT(MAX(v.viewed_at), \'%Y-%m-%d %H:%i:%s\') AS updated_at,
                   MAX(snap.store_name) AS store_name
            FROM fate_visit_log v
            LEFT JOIN spa_store_snapshots snap ON snap.spa_store_id = CAST(SUBSTRING(v.page_key FROM 7) AS UNSIGNED)
            WHERE v.page_key LIKE \'store.%\'
            GROUP BY spa_store_id
            HAVING spa_store_id > 0
            ORDER BY hit_count DESC, spa_store_id ASC
            LIMIT ' . $limit;
    $st = $pdo->query($sql);
    if (!$st) {
        return [];
    }
    $out = [];
    while ($row = $st->fetch(PDO::FETCH_ASSOC)) {
        if (!is_array($row)) {
            continue;
        }
        $sid = isset($row['spa_store_id']) ? (int) $row['spa_store_id'] : 0;
        if ($sid <= 0) {
            continue;
        }
        $name = isset($row['store_name']) && $row['store_name'] !== null && $row['store_name'] !== ''
            ? (string) $row['store_name']
            : null;
        $out[] = [
            'spa_store_id' => $sid,
            'hit_count' => isset($row['hit_count']) ? (int) $row['hit_count'] : 0,
            'updated_at' => isset($row['updated_at']) ? (string) $row['updated_at'] : '',
            'store_name' => $name,
        ];
    }

    return $out;
}

function fate_store_hit_stats_total_hits(PDO $pdo): int
{
    fate_visit_log_ensure_table($pdo);
    $n = $pdo->query(
        "SELECT COUNT(*) FROM fate_visit_log WHERE page_key LIKE 'store.%'"
    )->fetchColumn();

    return (int) $n;
}

function fate_store_hit_stats_row_count(PDO $pdo): int
{
    fate_visit_log_ensure_table($pdo);
    $n = $pdo->query(
        'SELECT COUNT(*) FROM (
            SELECT CAST(SUBSTRING(page_key FROM 7) AS UNSIGNED) AS sid
            FROM fate_visit_log
            WHERE page_key LIKE \'store.%\'
            GROUP BY sid
            HAVING sid > 0
        ) x'
    )->fetchColumn();

    return (int) $n;
}
