<?php

declare(strict_types=1);

require_once __DIR__ . DIRECTORY_SEPARATOR . 'admin_member_auth.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_member_password.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_member_register.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_public_session.php';

/**
 * 前台會員登入（與後台共用 members 表；任一 role 只要啟用欄位為 1 即可，與後台限 admin 不同）。
 *
 * @return array{ok:true,member:array{id:int,username:string,display_name:string,role:string}}|array{ok:false,err:'config'|'invalid'|'inactive'|'pending_approval'|'missing_table'|'bad_column'|'email_unverified'}
 */
function fate_public_member_authenticate(PDO $pdo, string $username, string $password): array
{
    if (!fate_admin_member_auth_ready()) {
        return ['ok' => false, 'err' => 'config'];
    }

    $u = trim($username);
    if ($u === '' || $password === '') {
        return ['ok' => false, 'err' => 'invalid'];
    }

    require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_member_email.php';
    fate_member_email_bootstrap_schema($pdo);

    try {
        $fetch = fate_public_member_login_row_fetch($pdo, $u);
    } catch (PDOException $e) {
        $msg = $e->getMessage();
        if (str_contains($msg, 'Base table or view not found') || str_contains($msg, "doesn't exist")) {
            return ['ok' => false, 'err' => 'missing_table'];
        }
        if (str_contains($msg, 'Unknown column') && str_contains($msg, FATE_MEMBER_ACTIVE_COLUMN)) {
            return ['ok' => false, 'err' => 'bad_column'];
        }
        throw $e;
    }

    if (!$fetch['found'] || !is_array($fetch['row'])) {
        return ['ok' => false, 'err' => 'invalid'];
    }

    $row = $fetch['row'];

    $hash = isset($row['password']) ? (string) $row['password'] : '';
    if ($hash === '' || !fate_member_verify_password($password, $hash)) {
        return ['ok' => false, 'err' => 'invalid'];
    }

    if (fate_member_row_pending_approval($row)) {
        return ['ok' => false, 'err' => 'pending_approval'];
    }

    if ((int) ($row['_mem_active'] ?? 0) !== 1) {
        return ['ok' => false, 'err' => 'inactive'];
    }

    $id = (int) ($row['id'] ?? 0);
    if ($id <= 0) {
        return ['ok' => false, 'err' => 'invalid'];
    }

    if (fate_member_requires_email_verify_for_row($pdo, $row)) {
        return ['ok' => false, 'err' => 'email_unverified'];
    }

    fate_admin_member_touch_last_login($pdo, $id);

    return [
        'ok' => true,
        'member' => [
            'id' => $id,
            'username' => (string) ($row['username'] ?? ''),
            'display_name' => isset($row['display_name']) ? (string) $row['display_name'] : '',
            'role' => isset($row['role']) ? trim((string) $row['role']) : '',
        ],
    ];
}

/** @param array{id:int,username:string,display_name:string,role?:string} $member */
function fate_public_member_bind_session(array $member, bool $emailVerified = true): void
{
    fate_public_session_start();
    $_SESSION['fate_site_member_id'] = $member['id'];
    $_SESSION['fate_site_username'] = $member['username'];
    $_SESSION['fate_site_display_name'] = $member['display_name'];
    $_SESSION['fate_site_email_verified'] = $emailVerified;
    $role = isset($member['role']) ? trim((string) $member['role']) : '';
    $_SESSION['fate_site_is_admin'] = fate_member_role_is_admin($role);
    session_regenerate_id(true);
    fate_session_touch_cookie();

    if ($_SESSION['fate_site_is_admin']) {
        $member['role'] = 'admin';
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_member_session_bridge.php';
        fate_member_bridge_sync_after_public_login($member);
    }
}

/**
 * 舊 Session 無 fate_site_is_admin 時，自資料庫補齊一次（避免須重新登入才能見後台連結）。
 */
function fate_public_member_ensure_admin_flag(PDO $pdo): void
{
    if (!fate_admin_member_auth_ready()) {
        return;
    }
    fate_public_session_start();
    $id = isset($_SESSION['fate_site_member_id']) ? (int) $_SESSION['fate_site_member_id'] : 0;
    if ($id <= 0) {
        return;
    }
    if (!empty($_SESSION['fate_site_is_admin'])) {
        return;
    }

    $db = FATE_MEMBER_DATABASE;
    $tbl = FATE_MEMBER_TABLE;
    $sql = sprintf(
        'SELECT `role` FROM `%s`.`%s` WHERE `id` = ? LIMIT 1',
        str_replace('`', '``', $db),
        str_replace('`', '``', $tbl)
    );

    try {
        $st = $pdo->prepare($sql);
        $st->execute([$id]);
        $roleRaw = $st->fetchColumn();
        $role = is_string($roleRaw) ? trim($roleRaw) : '';
        $_SESSION['fate_site_is_admin'] = fate_member_role_is_admin($role);
        if ($_SESSION['fate_site_is_admin']) {
            require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_member_session_bridge.php';
            fate_member_bridge_sync_after_public_login([
                'id' => $id,
                'username' => isset($_SESSION['fate_site_username']) ? (string) $_SESSION['fate_site_username'] : '',
                'display_name' => isset($_SESSION['fate_site_display_name']) ? (string) $_SESSION['fate_site_display_name'] : '',
                'role' => 'admin',
            ]);
        }
    } catch (Throwable) {
        $_SESSION['fate_site_is_admin'] = false;
    }
}

/** @return array{id:int,username:string,display_name:string,avatar_rel:string}|null */
function fate_public_member_current(): ?array
{
    fate_public_session_start();
    $id = isset($_SESSION['fate_site_member_id']) ? (int) $_SESSION['fate_site_member_id'] : 0;
    if ($id <= 0) {
        return null;
    }

    return [
        'id' => $id,
        'username' => isset($_SESSION['fate_site_username']) ? (string) $_SESSION['fate_site_username'] : '',
        'display_name' => isset($_SESSION['fate_site_display_name']) ? trim((string) $_SESSION['fate_site_display_name']) : '',
        'avatar_rel' => isset($_SESSION['fate_site_avatar_rel']) ? trim((string) $_SESSION['fate_site_avatar_rel']) : '',
    ];
}

function fate_public_member_display_label(?array $member): string
{
    if ($member === null) {
        return '';
    }
    $name = trim((string) ($member['display_name'] ?? ''));
    $user = trim((string) ($member['username'] ?? ''));
    if ($name !== '') {
        return $name;
    }
    if ($user !== '') {
        return $user;
    }

    return '會員';
}

function fate_public_member_csrf_token(): string
{
    fate_public_session_start();
    if (empty($_SESSION['fate_site_csrf'])) {
        $_SESSION['fate_site_csrf'] = bin2hex(random_bytes(24));
    }

    return (string) $_SESSION['fate_site_csrf'];
}

function fate_public_member_csrf_check(?string $token): bool
{
    return is_string($token) && isset($_SESSION['fate_site_csrf'])
        && hash_equals((string) $_SESSION['fate_site_csrf'], $token);
}

/**
 * 驗證登入／註冊成功後的站內回跳路徑（拒絕外部網址）。
 */
function fate_public_sanitize_login_next(?string $next): string
{
    $next = is_string($next) ? trim($next) : '';
    if ($next === '' || $next === '/') {
        return './';
    }
    if ($next === './' || str_starts_with($next, './?')) {
        return $next;
    }
    if (preg_match('#^\./store\.php\?id=\d+$#', $next) === 1) {
        return $next;
    }
    if (preg_match('#^\./[a-zA-Z0-9_-]+\.php(?:\?[\w&=%.\-+_]*)?$#', $next) === 1) {
        return $next;
    }
    if (preg_match('#^store\.php\?id=\d+$#', $next) === 1) {
        return $next;
    }
    if (preg_match('#^[a-zA-Z0-9_-]+\.php(?:\?[\w&=%.\-+_]*)?$#', $next) === 1) {
        return $next;
    }
    if (preg_match('#^forum/(?:[a-zA-Z0-9_-]+/)*[a-zA-Z0-9_-]+\.php(?:\?[\w&=%.\-+_]*)?$#', $next) === 1) {
        return $next;
    }

    return './';
}

/**
 * 依目前請求頁面組出 login.php?next= 用的回跳路徑（側欄登入用）。
 */
function fate_public_login_next_from_request(?string $fallback = './'): string
{
    $scriptFile = isset($_SERVER['SCRIPT_FILENAME']) ? str_replace('\\', '/', (string) $_SERVER['SCRIPT_FILENAME']) : '';
    $fateRoot = str_replace('\\', '/', dirname(__DIR__));

    if ($scriptFile !== '' && str_starts_with(strtolower($scriptFile), strtolower($fateRoot))) {
        $rel = ltrim(substr($scriptFile, strlen($fateRoot)), '/');
    } else {
        $script = isset($_SERVER['SCRIPT_NAME']) ? ltrim(str_replace('\\', '/', (string) $_SERVER['SCRIPT_NAME']), '/') : '';
        if ($script === '') {
            return fate_public_sanitize_login_next($fallback);
        }
        if (preg_match('#(forum/.+)$#', $script, $forumMatch)) {
            $rel = $forumMatch[1];
        } elseif (preg_match('#([a-zA-Z0-9_-]+\.php)$#', $script, $fileMatch)) {
            $rel = $fileMatch[1];
        } else {
            return fate_public_sanitize_login_next($fallback);
        }
    }

    $qs = isset($_SERVER['QUERY_STRING']) && (string) $_SERVER['QUERY_STRING'] !== ''
        ? '?' . (string) $_SERVER['QUERY_STRING'] : '';

    $base = strtolower(basename($rel));
    if (in_array($base, ['login.php', 'register.php', 'logout.php'], true)) {
        return fate_public_sanitize_login_next($fallback);
    }

    return fate_public_sanitize_login_next($rel . $qs);
}

/** 未登入則導向 login.php；$next 請傳站內相對路徑。 */
function fate_public_member_require_login(string $next = './'): void
{
    if (fate_public_member_current() !== null) {
        return;
    }
    header('Location: login.php?next=' . rawurlencode(fate_public_sanitize_login_next($next)), true, 302);
    exit;
}

function fate_public_member_session_email_verified(): bool
{
    fate_public_session_start();

    return !isset($_SESSION['fate_site_email_verified']) || $_SESSION['fate_site_email_verified'] !== false;
}

function fate_public_member_set_email_verified_session(bool $verified): void
{
    fate_public_session_start();
    $_SESSION['fate_site_email_verified'] = $verified;
}

/** 須已驗證 Email（若站台有啟用驗證）；否則導向我的帳號。 */
function fate_public_member_require_verified_email(string $next = './'): void
{
    fate_public_member_require_login($next);
    if (fate_public_member_session_email_verified()) {
        return;
    }
    header('Location: member_profile.php', true, 302);
    exit;
}

/**
 * 前台：目前登入會員是否可參與互動（已驗證且非黑名單；站方人員豁免）。
 */
function fate_public_member_can_interact(PDO $pdo): bool
{
    $member = fate_public_member_current();
    if ($member === null) {
        return false;
    }
    $memberId = (int) ($member['id'] ?? 0);
    if ($memberId <= 0) {
        return false;
    }
    if (!fate_public_member_session_email_verified()) {
        fate_public_member_ensure_admin_flag($pdo);
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_store_masseurs.php';
        if (fate_masseur_member_is_site_staff($pdo)) {
            return true;
        }

        return false;
    }

    require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_member_tier.php';

    return fate_member_can_interact($pdo, $memberId);
}

/** 須可參與互動；未驗證導向我的帳號完成驗證，黑名單等導向帳號頁並附提示。 */
function fate_public_member_require_interact(PDO $pdo, string $next = './'): void
{
    fate_public_member_require_login($next);
    if (fate_public_member_can_interact($pdo)) {
        return;
    }
    if (!fate_public_member_session_email_verified()) {
        header('Location: member_profile.php', true, 302);
        exit;
    }
    header('Location: member_profile.php?interact=denied', true, 302);
    exit;
}

/** 互動被拒時的友善訊息。 */
function fate_public_member_interact_denied_message(PDO $pdo): string
{
    if (!fate_public_member_session_email_verified()) {
        return '請先完成 Email 驗證後再參與互動。';
    }
    require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_member_tier.php';
    if (fate_public_member_is_blacklisted($pdo)) {
        return '您的帳號已被列入黑名單，無法參與互動。';
    }

    return '目前無法參與互動，請聯絡站方。';
}

/**
 * 更新會員顯示名稱（display_name）。
 *
 * @return array{ok:bool, error:?string}
 */
function fate_public_member_save_display_name(PDO $pdo, int $memberId, string $displayName): array
{
    if (!fate_admin_member_auth_ready() || $memberId <= 0) {
        return ['ok' => false, 'error' => '設定未就緒或會員無效。'];
    }
    $name = trim($displayName);
    if ($name !== '') {
        $dv = fate_public_member_validate_display_name_candidate($name);
        if (!$dv['ok']) {
            return ['ok' => false, 'error' => $dv['err'] ?? '顯示名稱格式不正確。'];
        }
        $name = $dv['display_name'];
    }

    $db = FATE_MEMBER_DATABASE;
    $tbl = FATE_MEMBER_TABLE;
    $sql = sprintf(
        'UPDATE `%s`.`%s` SET `display_name` = ? WHERE `id` = ? LIMIT 1',
        str_replace('`', '``', $db),
        str_replace('`', '``', $tbl)
    );
    try {
        $st = $pdo->prepare($sql);
        $st->execute([$name, $memberId]);
    } catch (Throwable $e) {
        return ['ok' => false, 'error' => $e->getMessage()];
    }

    fate_public_session_start();
    $_SESSION['fate_site_display_name'] = $name;

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