<?php

declare(strict_types=1);

header('Content-Type: text/html; charset=UTF-8');

require_once __DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'app.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_public_session.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_public_member_auth.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_member_profile.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_member_register.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_member_account.php';

fate_public_session_start();
fate_public_member_require_login(fate_public_login_next_from_request('member_profile.php'));

$pdo = null;
$dbErr = '';
try {
    require_once __DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'pdo.php';
    if (isset($GLOBALS['pdo']) && $GLOBALS['pdo'] instanceof PDO) {
        $pdo = $GLOBALS['pdo'];
    }
} catch (Throwable $e) {
    $dbErr = $e->getMessage();
}

$member = fate_public_member_current();
if ($member === null) {
    header(
        'Location: login.php?next=' . rawurlencode(fate_public_login_next_from_request('member_profile.php')),
        true,
        302
    );
    exit;
}

$memberId = (int) $member['id'];
$msgOk = '';
$msgBad = '';
$csrf = fate_public_member_csrf_token();
$profileTabAfterPost = null;

require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_ncoin_settings.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_ncoin_transfer.php';

if ($pdo instanceof PDO && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $tok = isset($_POST['csrf']) && is_string($_POST['csrf']) ? $_POST['csrf'] : '';
    $action = isset($_POST['action']) && is_string($_POST['action']) ? $_POST['action'] : 'save_profile';
    if ($action === 'ncoin_transfer') {
        $profileTabAfterPost = 'transfer';
    } elseif (in_array($action, ['save_profile', 'change_email', 'verify_code', 'resend_verify'], true)) {
        $profileTabAfterPost = 'profile';
    } elseif (in_array($action, ['favorite_experienced', 'favorites_public'], true)) {
        $profileTabAfterPost = 'favorites';
    } elseif (in_array($action, ['post_favorite_toggle', 'review_favorite_toggle'], true)) {
        $profileTabAfterPost = 'saved_threads';
    }
    if (!fate_public_member_csrf_check($tok)) {
        $msgBad = '工作階段逾期，請重新整理後再試。';
    } elseif (in_array($action, ['ncoin_transfer', 'favorite_experienced', 'favorites_public', 'post_favorite_toggle', 'review_favorite_toggle'], true)
        && $pdo instanceof PDO
        && !fate_public_member_can_interact($pdo)) {
        $msgBad = fate_public_member_interact_denied_message($pdo);
    } elseif ($action === 'resend_verify') {
        if (!function_exists('fate_verify_method_uses_token')) {
            require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_mail_settings.php';
        }
        if (!fate_verify_method_uses_token()) {
            $msgBad = '本站未啟用寄信驗證。';
        } else {
            $r = fate_member_resend_verify_for_member_id($pdo, $memberId);
            if ($r['ok']) {
                $msgOk = '驗證信已寄出；信內含驗證連結與 6 位驗證碼，擇一完成即可。';
            } else {
                $msgBad = $r['detail'] ?? '無法寄出驗證信。';
            }
        }
    } elseif ($action === 'verify_code') {
        if (!function_exists('fate_verify_method_uses_token')) {
            require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_mail_settings.php';
        }
        if (!fate_verify_method_uses_token()) {
            $msgBad = '本站未啟用驗證碼驗證。';
        } else {
        $codeRaw = isset($_POST['verify_code']) && is_string($_POST['verify_code']) ? $_POST['verify_code'] : '';
        $vr = fate_member_verify_email_with_code($pdo, $memberId, $codeRaw);
        if ($vr['ok'] && is_array($vr['member'] ?? null)) {
            require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_member_session_bridge.php';
            fate_session_assign_cookie_lifetime(fate_session_lifetime_sec());
            fate_public_member_bind_session($vr['member'], true);
            fate_member_profile_hydrate_session($pdo, $memberId);
            $_SESSION['fate_email_just_verified'] = true;
            header('Location: member_profile.php?tab=profile', true, 303);
            exit;
        }
        $msgBad = $vr['detail'] ?? '驗證失敗。';
        }
    } elseif ($action === 'ncoin_transfer') {
        $confirmed = !empty($_POST['transfer_confirm']);
        $toId = isset($_POST['to_member_id']) ? (int) $_POST['to_member_id'] : 0;
        $amount = isset($_POST['transfer_amount']) ? (int) $_POST['transfer_amount'] : 0;
        $note = isset($_POST['transfer_note']) && is_string($_POST['transfer_note']) ? $_POST['transfer_note'] : '';
        if (!$confirmed) {
            $msgBad = '請勾選「我已了解轉帳風險」後再送出。';
        } else {
            $tr = fate_ncoin_transfer_execute(
                $pdo,
                $memberId,
                $toId,
                $amount,
                (string) ($member['username'] ?? ''),
                $note
            );
            if ($tr['ok']) {
                $msgOk = '轉帳成功，已從您的帳戶扣除 ' . fate_ncoin_amount_with_unit($amount) . '。';
            } else {
                $msgBad = $tr['error'] ?? '轉帳失敗，請稍後再試。';
            }
        }
    } elseif ($action === 'favorite_experienced') {
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_masseur_favorites.php';
        $favMasseurId = isset($_POST['masseur_id']) ? (int) $_POST['masseur_id'] : 0;
        $experienced = !empty($_POST['experienced']);
        $fr = fate_masseur_favorite_set_experienced($pdo, $memberId, $favMasseurId, $experienced);
        if ($fr['ok']) {
            $msgOk = $experienced ? '已標記為已體驗。' : '已改為未體驗。';
        } else {
            $msgBad = $fr['error'] ?? '更新失敗。';
        }
    } elseif ($action === 'favorites_public') {
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_masseur_favorites.php';
        $isPublic = !empty($_POST['is_public']);
        $pr = fate_member_favorites_public_set($pdo, $memberId, $isPublic);
        if ($pr['ok']) {
            $msgOk = $isPublic ? '收藏清單已設為公開。' : '收藏清單已設為不公開。';
        } else {
            $msgBad = $pr['error'] ?? '儲存失敗。';
        }
    } elseif ($action === 'post_favorite_toggle') {
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_post_favorites.php';
        $favPostId = isset($_POST['post_id']) ? (int) $_POST['post_id'] : 0;
        $tr = fate_post_favorite_toggle($pdo, $memberId, $favPostId);
        if ($tr['ok']) {
            $msgOk = $tr['favorited'] ? '已加入收藏。' : '已取消收藏。';
        } else {
            $msgBad = $tr['error'] ?? '收藏操作失敗。';
        }
    } elseif ($action === 'review_favorite_toggle') {
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_masseur_review_engagement.php';
        $favReviewId = isset($_POST['review_id']) ? (int) $_POST['review_id'] : 0;
        $tr = fate_masseur_review_favorite_toggle($pdo, $memberId, $favReviewId);
        if ($tr['ok']) {
            $msgOk = $tr['favorited'] ? '已加入收藏。' : '已取消收藏。';
        } else {
            $msgBad = $tr['error'] ?? '收藏操作失敗。';
        }
    } elseif ($action === 'change_email') {
        $newEm = isset($_POST['new_email']) && is_string($_POST['new_email']) ? $_POST['new_email'] : '';
        $r = fate_member_change_email_unverified($pdo, $memberId, $newEm);
        if ($r['ok']) {
            $msgOk = $r['detail'] ?? '已更新 Email。';
            fate_public_member_sync_email_verified_session($pdo, $memberId);
        } else {
            $msgBad = $r['detail'] ?? '無法更新 Email。';
        }
    } else {
        $dn = isset($_POST['display_name']) && is_string($_POST['display_name']) ? $_POST['display_name'] : '';
        $sr = fate_public_member_save_display_name($pdo, $memberId, $dn);
        if (!$sr['ok']) {
            $msgBad = $sr['error'] ?? '無法更新顯示名稱。';
        } else {
            $msgOk = '已更新顯示名稱。';
            $member = fate_public_member_current();
        }
        $cropRaw = isset($_POST['avatar_crop']) && is_string($_POST['avatar_crop']) ? trim($_POST['avatar_crop']) : '';
        if ($cropRaw !== '') {
            $parsed = fate_member_profile_parse_avatar_data_url($cropRaw);
            if (!$parsed['ok']) {
                $msgBad = ($msgBad !== '' ? $msgBad . ' ' : '') . ($parsed['error'] ?? '頭像剪裁資料無效。');
            } else {
                $up = fate_member_profile_save_avatar_bytes($pdo, $memberId, $parsed['bytes']);
                if (!$up['ok']) {
                    $msgBad = ($msgBad !== '' ? $msgBad . ' ' : '') . ($up['error'] ?? '頭像上傳失敗。');
                } else {
                    $msgOk = ($msgOk !== '' ? $msgOk . ' ' : '') . '已更新頭像。';
                    $member = fate_public_member_current();
                }
            }
        } else {
            $av = $_FILES['avatar'] ?? null;
            $hasFile = is_array($av) && isset($av['error']) && (int) $av['error'] !== UPLOAD_ERR_NO_FILE;
            if ($hasFile) {
                $up = fate_member_profile_save_avatar($pdo, $memberId, $av);
                if (!$up['ok']) {
                    $msgBad = ($msgBad !== '' ? $msgBad . ' ' : '') . ($up['error'] ?? '頭像上傳失敗。');
                } else {
                    $msgOk = ($msgOk !== '' ? $msgOk . ' ' : '') . '已更新頭像。';
                    $member = fate_public_member_current();
                }
            }
        }
    }
}

if ($pdo instanceof PDO) {
    fate_public_member_ensure_admin_flag($pdo);
    fate_public_member_sync_email_verified_session($pdo, $memberId);
    fate_member_profile_hydrate_session($pdo, $memberId);
    $member = fate_public_member_current();
}

$emailStatus = $pdo instanceof PDO
    ? fate_member_account_email_status($pdo, $memberId)
    : ['enabled' => false, 'email' => '', 'masked' => '', 'verified' => true, 'verified_at' => null];

if (isset($_GET['resent']) && $_GET['resent'] === '1' && $msgOk === '') {
    $msgOk = '驗證信已寄出。';
}
if (isset($_GET['interact']) && $_GET['interact'] === 'denied' && $msgBad === '' && $pdo instanceof PDO) {
    $msgBad = fate_public_member_interact_denied_message($pdo);
}
$pendingApproval = false;
if ($pdo instanceof PDO && fate_member_manual_approval_enabled()) {
    require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_member_email.php';
    fate_member_email_bootstrap_schema($pdo);
    $approvalRow = fate_public_member_login_row_fetch_by_id($pdo, $memberId);
    if (!empty($approvalRow['found']) && is_array($approvalRow['row'] ?? null)) {
        $pendingApproval = fate_member_row_pending_approval($approvalRow['row']);
    }
}

if (isset($_GET['registered']) && $_GET['registered'] === '1' && $msgOk === '') {
    $verifyHint = isset($_GET['verify']) && is_string($_GET['verify']) ? $_GET['verify'] : '';
    if ($verifyHint === 'reverse') {
        $msgOk = '帳號已建立，請依下方說明從註冊信箱寄出驗證信，再按「我已寄出，立即驗證」完成驗證。';
    } elseif ($verifyHint === 'token') {
        $msgOk = '帳號已建立，驗證信已寄出；請至信箱點連結或輸入 6 位驗證碼完成驗證。';
    } elseif ($verifyHint === 'both') {
        $msgOk = '帳號已建立，驗證信已寄出；您也可依下方「回寄驗證」從註冊信箱寄信完成驗證（擇一即可）。';
    } elseif (isset($_GET['mail_err'])) {
        $msgOk = '帳號已建立，但驗證信未能寄出，請在此重寄或修改 Email。';
    } elseif ($pendingApproval) {
        $msgOk = '帳號已建立，尚待管理員審核。';
    } else {
        $msgOk = '帳號已建立，請完成 Email 驗證。';
    }
}

if (isset($_GET['submitted']) && $_GET['submitted'] === '1' && $msgOk === '') {
    $msgOk = '推薦店家已成功送出，待管理員審核。';
}

require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_admin_nav_visibility.php';
$fateAdminNavVisible = fate_admin_nav_visible_for_frontend();

$joinedLabel = '';
$userNcoin = null;
if ($pdo instanceof PDO) {
    $cols = fate_member_table_columns($pdo);
    if (isset($cols['created_at'])) {
        try {
            $dbEsc = str_replace('`', '``', FATE_MEMBER_DATABASE);
            $tblEsc = str_replace('`', '``', FATE_MEMBER_TABLE);
            $st = $pdo->prepare(sprintf('SELECT `created_at` FROM `%s`.`%s` WHERE `id` = ? LIMIT 1', $dbEsc, $tblEsc));
            $st->execute([$memberId]);
            $raw = $st->fetchColumn();
            if (is_string($raw) && trim($raw) !== '') {
                $joinedLabel = trim($raw);
            }
        } catch (Throwable) {
        }
    }
    $userNcoin = fate_member_read_ncoin($pdo, $memberId, (string) ($member['username'] ?? ''));
}

$ncoinTransferCfg = fate_ncoin_settings();
$ncoinTransferOpen = !empty($ncoinTransferCfg['transfer_enabled']);
$ncoinTransferEnabled = $ncoinTransferOpen && $userNcoin !== null;
$ncoinTransferEligible = false;
$ncoinTransferMax = 0;
if ($ncoinTransferEnabled && $pdo instanceof PDO) {
    $reserve = (int) ($ncoinTransferCfg['transfer_reserve'] ?? 0);
    $ncoinTransferEligible = fate_ncoin_transfer_eligible((int) $userNcoin, $reserve);
    $ncoinTransferMax = fate_ncoin_transfer_max_amount((int) $userNcoin, $reserve);
}

$hasNcoinTab = $pdo instanceof PDO && $userNcoin !== null;
$profileTabAllowed = ['profile', 'stores'];

require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_masseur_rewards.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_masseur_favorites.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_masseur_tags.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_plugins.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_member_tier.php';

$hasFavoritesTab = $pdo instanceof PDO && (fate_masseur_rewards_settings()['feature_enabled'] ?? false);
if ($hasFavoritesTab) {
    $profileTabAllowed[] = 'favorites';
}
$hasSavedThreadsTab = false;
$hasSavedForumPosts = $pdo instanceof PDO && fate_store_forum_plugin_active();
$hasSavedMasseurReviews = $pdo instanceof PDO && fate_masseur_reviews_enabled() && !fate_store_forum_plugin_active();
if ($hasSavedForumPosts || $hasSavedMasseurReviews) {
    $hasSavedThreadsTab = true;
    $profileTabAllowed[] = 'saved_threads';
}
if ($hasNcoinTab) {
    $profileTabAllowed[] = 'ncoin';
    $profileTabAllowed[] = 'transfer';
}

require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_member_stores.php';
$memberStores = $pdo instanceof PDO ? fate_member_stores_for_member($pdo, $memberId) : [];
$memberStoreCount = count($memberStores);

$storeSubmitted = isset($_GET['submitted']) && (string) $_GET['submitted'] === '1';

$profileTab = isset($_GET['tab']) && is_string($_GET['tab']) ? strtolower(trim($_GET['tab'])) : 'profile';
if ($storeSubmitted) {
    $profileTab = 'stores';
}
if (isset($_GET['hpage']) && !isset($_GET['tab']) && $hasNcoinTab) {
    $profileTab = 'ncoin';
}
if ($profileTabAfterPost !== null && in_array($profileTabAfterPost, $profileTabAllowed, true)) {
    $profileTab = $profileTabAfterPost;
}
if (!in_array($profileTab, $profileTabAllowed, true)) {
    $profileTab = 'profile';
}

$memberFavorites = [];
$memberFavoritesPublic = false;
$memberFavoritesCount = 0;
$favMasseurTagPayload = ['topics' => [], 'masseurs' => []];
$favReviewsPerPage = fate_masseur_reviews_per_page();
$favForumActive = fate_store_forum_plugin_active();
$favReviewsEnabled = $favForumActive ? false : fate_masseur_reviews_enabled();
$favCanInteract = $pdo instanceof PDO && fate_public_member_can_interact($pdo);
$favViewerIsStaff = $pdo instanceof PDO && fate_masseur_member_is_site_staff($pdo);
$favCanViewReviews = $favReviewsEnabled;
$favTagInteract = $favCanInteract;
$favHasTags = false;
$favCanViewTags = false;
$favTagsFormalOnly = fate_masseur_tags_formal_only();
$favCanAccessTagsFormal = true;
$favReturnTo = 'member_profile.php?tab=favorites';
$memberCanInteract = $pdo instanceof PDO && fate_public_member_can_interact($pdo);
$buildFavReviewPageUrl = static function (int $masseurId, int $page): string {
    $q = $_GET;
    $q['tab'] = 'favorites';
    if ($page > 1) {
        $q['rpage_' . $masseurId] = (string) max(1, $page);
    } else {
        unset($q['rpage_' . $masseurId]);
    }

    return 'member_profile.php?' . http_build_query($q) . '#fav-' . $masseurId;
};

if ($hasFavoritesTab && $pdo instanceof PDO) {
    fate_masseur_favorite_ensure_tables($pdo);
    $memberFavorites = fate_masseur_favorites_list_for_member($pdo, $memberId);
    $memberFavoritesCount = count($memberFavorites);
    $memberFavoritesPublic = fate_member_favorites_public_get($pdo, $memberId);
    $favMasseurIds = array_map(static fn (array $row): int => (int) ($row['masseur_id'] ?? 0), $memberFavorites);
    if ($favMasseurIds !== []) {
        $favMasseurTagPayload = fate_masseur_tag_store_payload($pdo, $favMasseurIds, $memberId);
    }
    $favHasTags = ($favMasseurTagPayload['topics'] ?? []) !== [];
    $favCanAccessTagsFormal = !$favTagsFormalOnly || fate_member_can_access_formal_only($pdo, $memberId, true);
    $favCanViewTags = $favHasTags && $favCanAccessTagsFormal;
    $favTagInteract = $favCanInteract && $favCanAccessTagsFormal;
}

if ($profileTab === 'favorites') {
    if (isset($_SESSION['fate_masseur_flash_ok']) && is_string($_SESSION['fate_masseur_flash_ok']) && $msgOk === '') {
        $msgOk = $_SESSION['fate_masseur_flash_ok'];
    }
    if (isset($_SESSION['fate_masseur_flash_bad']) && is_string($_SESSION['fate_masseur_flash_bad']) && $msgBad === '') {
        $msgBad = $_SESSION['fate_masseur_flash_bad'];
    }
    unset($_SESSION['fate_masseur_flash_ok'], $_SESSION['fate_masseur_flash_bad']);
}

$memberSavedThreads = [];
$memberSavedThreadsCount = 0;
$memberSavedReviews = [];
$memberSavedReviewsCount = 0;
if ($hasSavedThreadsTab && $pdo instanceof PDO) {
    if ($hasSavedForumPosts) {
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_post_favorites.php';
        fate_post_favorite_ensure_tables($pdo);
        $forumPerPage = 20;
        $forumCfgFile = __DIR__ . DIRECTORY_SEPARATOR . 'forum' . DIRECTORY_SEPARATOR . 'config.php';
        if (is_file($forumCfgFile)) {
            require_once $forumCfgFile;
            if (function_exists('forum_config')) {
                $forumPerPage = max(1, (int) (forum_config()['post_per_page'] ?? 20));
            }
        }
        $memberSavedThreads = fate_post_favorites_list_for_member($pdo, $memberId, $forumPerPage);
        $memberSavedThreadsCount = count($memberSavedThreads);
    }
    if ($hasSavedMasseurReviews) {
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_masseur_review_engagement.php';
        $memberSavedReviews = fate_masseur_review_favorites_list_for_member($pdo, $memberId, 100);
        $memberSavedReviewsCount = count($memberSavedReviews);
    }
}
$memberSavedArticlesCount = $memberSavedThreadsCount + $memberSavedReviewsCount;

if ($storeSubmitted && $msgOk === '') {
    $msgOk = '店家推薦已送出，待管理員審核通過後即會公開。';
}

$buildProfileTabUrl = static function (string $tab) use ($profileTabAllowed): string {
    if (!in_array($tab, $profileTabAllowed, true)) {
        $tab = 'profile';
    }

    return 'member_profile.php?tab=' . rawurlencode($tab);
};

require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_ncoin_history.php';
$ncoinHistory = [];
$ncoinSummary = ['earned' => 0, 'reverted' => 0, 'net' => 0, 'count' => 0];
$ncoinPerPage = 20;
$ncoinPage = isset($_GET['hpage']) ? max(1, (int) $_GET['hpage']) : 1;
$ncoinTotal = 0;
$ncoinPages = 1;
if ($pdo instanceof PDO) {
    $ncoinSummary = fate_ncoin_history_summary_for_member($pdo, $memberId);
    $ncoinTotal = $ncoinSummary['count'];
    $ncoinPages = max(1, (int) ceil($ncoinTotal / $ncoinPerPage));
    if ($ncoinPage > $ncoinPages) {
        $ncoinPage = $ncoinPages;
    }
    $ncoinHistory = fate_ncoin_history_for_member($pdo, $memberId, $ncoinPerPage, ($ncoinPage - 1) * $ncoinPerPage);
}

$ncoinRangeFrom = $ncoinTotal > 0 ? (($ncoinPage - 1) * $ncoinPerPage + 1) : 0;
$ncoinRangeTo = $ncoinTotal > 0 ? min($ncoinPage * $ncoinPerPage, $ncoinTotal) : 0;
$buildNcoinPageUrl = static function (int $page): string {
    $qs = ['tab' => 'ncoin'];
    if ($page > 1) {
        $qs['hpage'] = $page;
    }

    return 'member_profile.php?' . http_build_query($qs);
};
$ncoinPagerItems = [];
if ($ncoinPages > 1) {
    $window = 2;
    $nums = [1];
    $lo = max(2, $ncoinPage - $window);
    $hi = min($ncoinPages - 1, $ncoinPage + $window);
    for ($i = $lo; $i <= $hi; $i++) {
        $nums[] = $i;
    }
    if ($ncoinPages > 1) {
        $nums[] = $ncoinPages;
    }
    $nums = array_values(array_unique($nums));
    sort($nums, SORT_NUMERIC);
    $prev = 0;
    foreach ($nums as $n) {
        if ($prev > 0 && $n - $prev > 1) {
            $ncoinPagerItems[] = null;
        }
        $ncoinPagerItems[] = $n;
        $prev = $n;
    }
}

$avatarUrl = fate_member_profile_avatar_url($member['avatar_rel'] ?? '');
$disp = fate_public_member_display_label($member);
$showVerifiedBanner = !empty($_SESSION['fate_email_just_verified']);
unset($_SESSION['fate_email_just_verified']);
$needsVerify = $emailStatus['enabled'] && !$emailStatus['verified'];

require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_mail_settings.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_member_email_reverse.php';
$verifyUsesToken = fate_verify_method_uses_token();
$verifyUsesReverse = fate_verify_method_uses_reverse();
if (!$verifyUsesToken && !$verifyUsesReverse) {
    $verifyUsesToken = true;
}
$verifyReverseOnly = $verifyUsesReverse && !$verifyUsesToken;
$verifyTokenOnly = $verifyUsesToken && !$verifyUsesReverse;

$fatePublicMember = $member;
$ncoinName = fate_ncoin_display_name();
$ncoinNameEsc = htmlspecialchars($ncoinName, ENT_QUOTES, 'UTF-8');

require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_template.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_page_counter.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_public_list_query.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_search_probe.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_ui_settings.php';

$cityFilter = isset($_GET['city']) ? trim((string) $_GET['city']) : '';
if (strlen($cityFilter) > 120) {
    $cityFilter = substr($cityFilter, 0, 120);
}
$sort = isset($_GET['sort']) ? strtolower(trim((string) $_GET['sort'])) : 'rating';
if (!in_array($sort, ['rating', 'name', 'recent'], true)) {
    $sort = 'rating';
}
$storeSearchQ = fate_search_normalize_public_query(isset($_GET['q']) ? (string) $_GET['q'] : '');
$buildHomeQuery = static function (array $overrides): string {
    return fate_build_home_list_query($_GET, $overrides);
};
$storeSearchTotal = null;

$fateBrandForTitle = fate_public_site_branding();
$fatePageTitle = '我的帳號 — ' . $disp . ' — ' . $fateBrandForTitle['title_main'];
require_once __DIR__ . DIRECTORY_SEPARATOR . 'includes' . DIRECTORY_SEPARATOR . 'fate_seo.php';
$fateSeoContext = fate_seo_private_page_context();
$fateMemberIdForLog = fate_page_stats_member_id($fatePublicMember);
$fatePageStatsFooter = fate_page_stats_for_footer($pdo, 'member.profile', $fateMemberIdForLog, $fatePageTitle);
$fatePageHitCount = $fatePageStatsFooter['fatePageHitCount'];
$fatePageStatsDbOk = $fatePageStatsFooter['fatePageStatsDbOk'];

fate_render_member_profile(compact(
    'member',
    'pdo',
    'msgOk',
    'msgBad',
    'dbErr',
    'csrf',
    'emailStatus',
    'showVerifiedBanner',
    'needsVerify',
    'verifyReverseOnly',
    'verifyTokenOnly',
    'verifyUsesToken',
    'verifyUsesReverse',
    'pendingApproval',
    'joinedLabel',
    'userNcoin',
    'ncoinName',
    'ncoinNameEsc',
    'ncoinTransferCfg',
    'ncoinTransferOpen',
    'ncoinTransferEnabled',
    'ncoinTransferEligible',
    'ncoinTransferMax',
    'hasNcoinTab',
    'memberCanInteract',
    'profileTab',
    'profileTabAllowed',
    'buildProfileTabUrl',
    'ncoinSummary',
    'ncoinHistory',
    'ncoinPage',
    'ncoinPages',
    'ncoinTotal',
    'ncoinRangeFrom',
    'ncoinRangeTo',
    'buildNcoinPageUrl',
    'ncoinPagerItems',
    'avatarUrl',
    'disp',
    'fatePublicMember',
    'cityFilter',
    'sort',
    'storeSearchQ',
    'buildHomeQuery',
    'storeSearchTotal',
    'memberStores',
    'memberStoreCount',
    'hasFavoritesTab',
    'hasSavedThreadsTab',
    'memberFavorites',
    'memberFavoritesPublic',
    'memberFavoritesCount',
    'memberSavedThreads',
    'memberSavedThreadsCount',
    'memberSavedReviews',
    'memberSavedReviewsCount',
    'memberSavedArticlesCount',
    'hasSavedForumPosts',
    'hasSavedMasseurReviews',
    'favMasseurTagPayload',
    'favReviewsPerPage',
    'favForumActive',
    'favReviewsEnabled',
    'favCanInteract',
    'favCanViewReviews',
    'favCanViewTags',
    'favTagInteract',
    'favHasTags',
    'favTagsFormalOnly',
    'favCanAccessTagsFormal',
    'favReturnTo',
    'buildFavReviewPageUrl',
    'fateAdminNavVisible',
    'fatePageHitCount',
    'fatePageStatsDbOk',
    'fatePageTitle',
    'fateSeoContext'
));
