<?php

declare(strict_types=1);

/**
 * 會員回報店況／技師狀態（非肯定、僅顯示「X名會員」回報）。
 *
 * 店家：歇業、更名
 * 技師：已離店
 */

const FATE_CROWD_ENTITY_STORE = 'store';
const FATE_CROWD_ENTITY_MASSEUR = 'masseur';

function fate_crowd_report_ledger_action(string $entityType, string $reportKey): ?string
{
    if ($entityType === FATE_CROWD_ENTITY_STORE) {
        return match ($reportKey) {
            'closed' => 'crowd_store_closed',
            'renamed' => 'crowd_store_renamed',
            default => null,
        };
    }
    if ($entityType === FATE_CROWD_ENTITY_MASSEUR) {
        return match ($reportKey) {
            'left_store' => 'crowd_masseur_left_store',
            default => null,
        };
    }

    return null;
}

function fate_crowd_report_reward_grant(
    PDO $pdo,
    int $memberId,
    string $entityType,
    string $reportKey,
    int $reportRowId,
    string $username = ''
): int {
    $action = fate_crowd_report_ledger_action($entityType, $reportKey);
    if ($action === null || $reportRowId <= 0) {
        return 0;
    }
    require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_masseur_rewards.php';
    $label = fate_crowd_report_label($entityType, $reportKey);

    return fate_masseur_reward_grant($pdo, $memberId, $action, $reportRowId, $username, '會員回報：' . $label);
}

function fate_crowd_report_reward_revert(
    PDO $pdo,
    string $entityType,
    string $reportKey,
    int $reportRowId,
    string $username = ''
): int {
    $action = fate_crowd_report_ledger_action($entityType, $reportKey);
    if ($action === null || $reportRowId <= 0) {
        return 0;
    }
    require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_masseur_rewards.php';

    return fate_masseur_reward_revert($pdo, $action, $reportRowId, $username);
}

/** @return array<string,string> */
function fate_crowd_report_store_keys(): array
{
    return [
        'closed' => '歇業',
        'renamed' => '更名',
    ];
}

/** @return array<string,string> */
function fate_crowd_report_masseur_keys(): array
{
    return [
        'left_store' => '已離店',
    ];
}

function fate_crowd_report_label(string $entityType, string $reportKey): string
{
    $map = $entityType === FATE_CROWD_ENTITY_MASSEUR
        ? fate_crowd_report_masseur_keys()
        : fate_crowd_report_store_keys();

    return $map[$reportKey] ?? $reportKey;
}

function fate_crowd_report_wants_json(): bool
{
    if (isset($_POST['ajax']) && (string) $_POST['ajax'] === '1') {
        return true;
    }
    $accept = isset($_SERVER['HTTP_ACCEPT']) ? (string) $_SERVER['HTTP_ACCEPT'] : '';

    return str_contains($accept, 'application/json');
}

/** @param array<string, mixed> $payload */
function fate_crowd_report_json_send(array $payload, int $status = 200): void
{
    http_response_code($status);
    header('Content-Type: application/json; charset=UTF-8');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE);
    exit;
}

function fate_crowd_report_key_valid(string $entityType, string $reportKey): bool
{
    $map = $entityType === FATE_CROWD_ENTITY_MASSEUR
        ? fate_crowd_report_masseur_keys()
        : fate_crowd_report_store_keys();

    return isset($map[$reportKey]);
}

function fate_crowd_report_ensure_table(PDO $pdo): void
{
    static $done = false;
    if ($done) {
        return;
    }
    $done = true;
    try {
        $pdo->exec(
            <<<'SQL'
CREATE TABLE IF NOT EXISTS `fate_crowd_reports` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `entity_type` VARCHAR(16) NOT NULL,
  `entity_id` INT UNSIGNED NOT NULL,
  `report_key` VARCHAR(32) NOT NULL,
  `member_id` INT UNSIGNED NOT NULL,
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uq_crowd_report` (`entity_type`, `entity_id`, `report_key`, `member_id`),
  KEY `idx_crowd_entity` (`entity_type`, `entity_id`, `report_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
SQL
        );
    } catch (Throwable $e) {
        error_log('fate_crowd_report_ensure_table: ' . $e->getMessage());
    }
}

/**
 * @return array{ok:bool,error?:string,picked?:bool,member_count?:int,reward_amount?:int,reward_reverted?:int}
 */
function fate_crowd_report_toggle(
    PDO $pdo,
    string $entityType,
    int $entityId,
    string $reportKey,
    int $memberId,
    string $username = ''
): array {
    if ($memberId <= 0 || $entityId <= 0 || !fate_crowd_report_key_valid($entityType, $reportKey)) {
        return ['ok' => false, 'error' => '參數錯誤。'];
    }

    fate_crowd_report_ensure_table($pdo);

    if ($entityType === FATE_CROWD_ENTITY_MASSEUR) {
        require_once __DIR__ . DIRECTORY_SEPARATOR . 'fate_store_masseurs.php';
        $row = fate_masseur_get($pdo, $entityId);
        if ($row === null || (int) ($row['is_hidden'] ?? 0) === 1) {
            return ['ok' => false, 'error' => '找不到技師。'];
        }
    } elseif ($entityType === FATE_CROWD_ENTITY_STORE) {
        try {
            $st = $pdo->prepare('SELECT `spa_store_id` FROM `spa_store_snapshots` WHERE `spa_store_id` = ? AND `is_approved` = 1 LIMIT 1');
            $st->execute([$entityId]);
            if ($st->fetchColumn() === false) {
                return ['ok' => false, 'error' => '找不到店家。'];
            }
        } catch (Throwable) {
            return ['ok' => false, 'error' => '無法驗證店家。'];
        }
    } else {
        return ['ok' => false, 'error' => '無效的類型。'];
    }

    try {
        $find = $pdo->prepare(
            'SELECT `id` FROM `fate_crowd_reports`
             WHERE `entity_type` = ? AND `entity_id` = ? AND `report_key` = ? AND `member_id` = ?
             LIMIT 1'
        );
        $find->execute([$entityType, $entityId, $reportKey, $memberId]);
        $existingId = $find->fetchColumn();

        $rewardAmount = 0;
        $rewardReverted = 0;

        if ($existingId !== false) {
            $reportId = (int) $existingId;
            $rewardReverted = fate_crowd_report_reward_revert($pdo, $entityType, $reportKey, $reportId, $username);
            $pdo->prepare('DELETE FROM `fate_crowd_reports` WHERE `id` = ?')->execute([$reportId]);
            $picked = false;
        } else {
            $pdo->prepare(
                'INSERT INTO `fate_crowd_reports` (`entity_type`, `entity_id`, `report_key`, `member_id`) VALUES (?, ?, ?, ?)'
            )->execute([$entityType, $entityId, $reportKey, $memberId]);
            $reportId = (int) $pdo->lastInsertId();
            $rewardAmount = fate_crowd_report_reward_grant($pdo, $memberId, $entityType, $reportKey, $reportId, $username);
            $picked = true;
        }

        $cntSt = $pdo->prepare(
            'SELECT COUNT(*) FROM `fate_crowd_reports`
             WHERE `entity_type` = ? AND `entity_id` = ? AND `report_key` = ?'
        );
        $cntSt->execute([$entityType, $entityId, $reportKey]);

        return [
            'ok' => true,
            'picked' => $picked,
            'member_count' => (int) $cntSt->fetchColumn(),
            'reward_amount' => $rewardAmount,
            'reward_reverted' => $rewardReverted,
        ];
    } catch (Throwable $e) {
        return ['ok' => false, 'error' => '操作失敗：' . $e->getMessage()];
    }
}

/**
 * @param list<int> $entityIds
 * @return array<int, array<string, array{count:int,viewer:bool}>>
 */
function fate_crowd_report_batch_payload(PDO $pdo, string $entityType, array $entityIds, int $memberId = 0): array
{
    $keys = $entityType === FATE_CROWD_ENTITY_MASSEUR
        ? array_keys(fate_crowd_report_masseur_keys())
        : array_keys(fate_crowd_report_store_keys());

    $ids = array_values(array_unique(array_filter(array_map('intval', $entityIds), static fn (int $id): bool => $id > 0)));
    $out = [];
    foreach ($ids as $id) {
        $out[$id] = [];
        foreach ($keys as $k) {
            $out[$id][$k] = ['count' => 0, 'viewer' => false];
        }
    }
    if ($ids === []) {
        return $out;
    }

    fate_crowd_report_ensure_table($pdo);

    try {
        $ph = implode(',', array_fill(0, count($ids), '?'));
        $st = $pdo->prepare(
            "SELECT `entity_id`, `report_key`, COUNT(*) AS `cnt`
             FROM `fate_crowd_reports`
             WHERE `entity_type` = ? AND `entity_id` IN ($ph)
             GROUP BY `entity_id`, `report_key`"
        );
        $st->execute(array_merge([$entityType], $ids));
        while ($row = $st->fetch(PDO::FETCH_ASSOC)) {
            if (!is_array($row)) {
                continue;
            }
            $eid = (int) ($row['entity_id'] ?? 0);
            $rk = (string) ($row['report_key'] ?? '');
            if ($eid > 0 && isset($out[$eid][$rk])) {
                $out[$eid][$rk]['count'] = (int) ($row['cnt'] ?? 0);
            }
        }

        if ($memberId > 0) {
            $st2 = $pdo->prepare(
                "SELECT `entity_id`, `report_key` FROM `fate_crowd_reports`
                 WHERE `entity_type` = ? AND `member_id` = ? AND `entity_id` IN ($ph)"
            );
            $st2->execute(array_merge([$entityType, $memberId], $ids));
            while ($row = $st2->fetch(PDO::FETCH_ASSOC)) {
                if (!is_array($row)) {
                    continue;
                }
                $eid = (int) ($row['entity_id'] ?? 0);
                $rk = (string) ($row['report_key'] ?? '');
                if ($eid > 0 && isset($out[$eid][$rk])) {
                    $out[$eid][$rk]['viewer'] = true;
                }
            }
        }
    } catch (Throwable $e) {
        error_log('fate_crowd_report_batch_payload: ' . $e->getMessage());
    }

    return $out;
}

function fate_crowd_report_split_return(string $raw): array
{
    $raw = trim($raw);
    $anchor = '';
    if (str_contains($raw, '#')) {
        [$raw, $anchor] = explode('#', $raw, 2);
        $anchor = trim($anchor);
    }

    return [$raw, $anchor];
}

function fate_crowd_report_sanitize_anchor(?string $raw): string
{
    $raw = trim((string) $raw);
    if ($raw === '' || preg_match('#^[A-Za-z0-9_\-]+$#', $raw) !== 1) {
        return '';
    }

    return $raw;
}

function fate_crowd_report_sanitize_return_to(?string $raw): string
{
    [$raw, ] = fate_crowd_report_split_return((string) $raw);
    $raw = trim($raw);
    if ($raw === '' || str_contains($raw, '..')) {
        return 'index.php';
    }
    if (preg_match('#^https?://[^/]+(.*)$#i', $raw, $m) === 1) {
        $raw = ltrim($m[1], '/');
    }
    if ($raw === '' || preg_match('#^https?://#i', $raw) === 1) {
        return 'index.php';
    }
    if (preg_match('#^(?:\./)?(?:index\.php|store\.php)(?:\?[^#]*)?$#', $raw) === 1) {
        return str_starts_with($raw, './') ? substr($raw, 2) : $raw;
    }
    if (preg_match('#^forum/thread\.php\?id=\d+$#', $raw) === 1) {
        return $raw;
    }

    return 'index.php';
}

/**
 * @return array{0:string,1:string} path, anchor
 */
function fate_crowd_report_resolve_redirect_target(string $returnTo, string $returnAnchor = ''): array
{
    [$path, $anchorFromUrl] = fate_crowd_report_split_return($returnTo);
    $target = fate_crowd_report_sanitize_return_to($path);
    $anchor = fate_crowd_report_sanitize_anchor($returnAnchor !== '' ? $returnAnchor : $anchorFromUrl);
    if (!str_starts_with($target, '../') && !str_starts_with($target, './') && $target[0] !== '/') {
        $target = '../' . ltrim($target, '/');
    }
    if ($anchor !== '') {
        $target .= '#' . $anchor;
    }

    return [$target, fate_crowd_report_sanitize_return_to($path)];
}

/**
 * @param array<string, array{count:int,viewer:bool}> $entityData
 */
function fate_crowd_report_render_block(
    string $entityType,
    int $entityId,
    array $entityData,
    bool $canInteract,
    string $csrf,
    array $reportKeys,
    string $apiAction = 'api/crowd_report.php',
    ?string $returnTo = null,
    string $modifierClass = '',
    ?string $returnAnchor = null
): void {
    if ($entityId <= 0) {
        return;
    }

    $hasAny = false;
    $totalReportCount = 0;
    foreach ($reportKeys as $rk) {
        $cnt = (int) ($entityData[$rk]['count'] ?? 0);
        $viewer = !empty($entityData[$rk]['viewer']);
        $totalReportCount += $cnt;
        if ($cnt > 0 || $viewer) {
            $hasAny = true;
            continue;
        }
        if ($canInteract) {
            $hasAny = true;
        }
    }
    if (!$hasAny) {
        return;
    }

    $hasCommunityReports = $totalReportCount > 0;

    $mod = trim($modifierClass);
    $reportKeysJson = json_encode(array_values($reportKeys), JSON_UNESCAPED_UNICODE);
    if (!is_string($reportKeysJson)) {
        $reportKeysJson = '[]';
    }
    ?>
    <div class="crowd-reports<?= $mod !== '' ? ' ' . htmlspecialchars($mod, ENT_QUOTES, 'UTF-8') : '' ?><?= $hasCommunityReports ? '' : ' crowd-reports--no-signal' ?>"
         role="group"
         aria-label="會員回報"
         <?php if ($canInteract): ?>
         data-crowd-interactive="1"
         data-crowd-api="<?= htmlspecialchars($apiAction, ENT_QUOTES, 'UTF-8') ?>"
         data-crowd-csrf="<?= htmlspecialchars($csrf, ENT_QUOTES, 'UTF-8') ?>"
         data-entity-type="<?= htmlspecialchars($entityType, ENT_QUOTES, 'UTF-8') ?>"
         data-entity-id="<?= (int) $entityId ?>"
         data-report-keys="<?= htmlspecialchars($reportKeysJson, ENT_QUOTES, 'UTF-8') ?>"
         <?php endif; ?>>
        <div class="crowd-reports__flash" hidden aria-live="polite"></div>
        <?php if ($hasCommunityReports): ?>
            <p class="crowd-reports__hint crowd-reports__hint--signal">會員回報，不代表本站確認</p>
        <?php elseif ($canInteract): ?>
            <p class="crowd-reports__hint crowd-reports__hint--action">
                <span class="crowd-reports__hint-long">發現資訊有誤？可點選回報（不代表本站確認）</span>
                <span class="crowd-reports__hint-short">資訊有誤？點選回報</span>
            </p>
        <?php endif; ?>
        <div class="crowd-reports__list">
            <?php foreach ($reportKeys as $reportKey): ?>
                <?php
                $label = fate_crowd_report_label($entityType, $reportKey);
                $cnt = (int) ($entityData[$reportKey]['count'] ?? 0);
                $picked = !empty($entityData[$reportKey]['viewer']);
                $showAsTag = $cnt > 0 || $picked;
                if ($cnt <= 0 && !$picked && !$canInteract) {
                    continue;
                }
                ?>
                <?php if ($canInteract): ?>
                    <?php if ($showAsTag): ?>
                        <button type="button"
                                class="crowd-report-btn crowd-report-tag<?= $picked ? ' is-active' : '' ?> has-count"
                                data-report-key="<?= htmlspecialchars($reportKey, ENT_QUOTES, 'UTF-8') ?>"
                                data-label="<?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>"
                                data-count="<?= (int) $cnt ?>"
                                data-picked="<?= $picked ? '1' : '0' ?>"
                                title="回報<?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>（再次點擊可取消）">
                            <span class="crowd-report-tag__label"><?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?></span>
                            <?php if ($cnt > 0): ?>
                                <span class="crowd-report-tag__members"><?= (int) $cnt ?>名會員</span>
                            <?php endif; ?>
                        </button>
                    <?php else: ?>
                        <button type="button"
                                class="crowd-report-btn crowd-report-action"
                                data-report-key="<?= htmlspecialchars($reportKey, ENT_QUOTES, 'UTF-8') ?>"
                                data-label="<?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>"
                                data-count="0"
                                data-picked="0"
                                title="回報<?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>">
                            回報<?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>
                        </button>
                    <?php endif; ?>
                <?php else: ?>
                    <?php if ($cnt > 0): ?>
                        <span class="crowd-report-tag is-static has-count">
                            <span class="crowd-report-tag__label"><?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?></span>
                            <span class="crowd-report-tag__members"><?= (int) $cnt ?>名會員</span>
                        </span>
                    <?php endif; ?>
                <?php endif; ?>
            <?php endforeach; ?>
        </div>
    </div>
    <?php
}
