<script setup lang="ts">
import { formatServerDateTime } from '@sammo-ts/common';
import { computed, onMounted, ref } from 'vue';
import TournamentBracket from '../components/tournament/TournamentBracket.vue';
import TournamentPageHeader from '../components/tournament/TournamentPageHeader.vue';
import GeneralIdentity from '../components/ui/GeneralIdentity.vue';
import { trpc } from '../utils/trpc';

type Snapshot = Awaited<ReturnType<typeof trpc.tournament.getSnapshot.query>>;

const snapshot = ref<Snapshot | null>(null);
const summary = ref<Awaited<ReturnType<typeof trpc.tournament.getBettingSummary.query>> | null>(null);
const rankings = ref<Awaited<ReturnType<typeof trpc.tournament.getRankings.query>>>([]);
const loading = ref(false);
const error = ref<string | null>(null);
const message = ref<string | null>(null);
const amounts = ref<Record<number, number>>({});
const activeRankingPrefix = ref('tt');
const typeNames = ['전력전', '통솔전', '일기토', '설전'];
const stageNames = [
    '경기 없음',
    '참가 모집중',
    '예선 진행중',
    '본선 추첨중',
    '본선 진행중',
    '16강 배정중',
    '베팅 진행중',
    '16강 진행중',
    '8강 진행중',
    '4강 진행중',
    '결승 진행중',
];

const errorText = (value: unknown) => (value instanceof Error ? value.message : String(value));
const load = async () => {
    loading.value = true;
    error.value = null;
    try {
        [snapshot.value, summary.value, rankings.value] = await Promise.all([
            trpc.tournament.getSnapshot.query(),
            trpc.tournament.getBettingSummary.query(),
            trpc.tournament.getRankings.query(),
        ]);
    } catch (value) {
        error.value = errorText(value);
    } finally {
        loading.value = false;
    }
};
onMounted(() => void load());

const participantMap = computed(
    () => new Map((snapshot.value?.participants ?? []).map((participant) => [participant.id, participant]))
);
const final16Ids = computed(() =>
    (snapshot.value?.matches ?? [])
        .filter((match) => match.stage === 7)
        .sort((a, b) => a.roundIndex - b.roundIndex)
        .flatMap((match) => [match.attackerId, match.defenderId])
);
const candidates = computed(() =>
    Array.from({ length: 16 }, (_, index) => {
        const id = final16Ids.value[index] ?? 0;
        const participant = id ? participantMap.value.get(id) : null;
        return {
            id,
            name: id ? (participant?.name ?? `#${id}`) : '-',
            picture: participant?.picture ?? null,
            imageServer: participant?.imageServer ?? 0,
        };
    })
);
const totalAmount = computed(() => summary.value?.totalAmount ?? 0);
const myAmount = computed(() => summary.value?.myAmount ?? 0);
const betTotals = computed(() => summary.value?.totals as Record<number, number> | undefined);
const myBetTotals = computed(() => summary.value?.myTotals as Record<number, number> | undefined);
const ratio = (id: number) => {
    const totals = summary.value?.totals as Record<number, number> | undefined;
    const amount = totals?.[id] ?? 0;
    return amount ? (totalAmount.value / amount).toFixed(2) : '0';
};
const openingTime = computed(() =>
    formatServerDateTime(snapshot.value?.state?.nextAt, { format: 'hourMinute', fallback: '--:--' })
);
const expected = (id: number) => {
    const myTotals = summary.value?.myTotals as Record<number, number> | undefined;
    const current = myTotals?.[id] ?? 0;
    const numericRatio = Number(ratio(id));
    return Number.isFinite(numericRatio) ? Math.floor(current * numericRatio) : 0;
};
const bettingOpen = computed(() => {
    const state = snapshot.value?.state;
    if (!state || state.stage !== 6) return false;
    if (!state.bettingCloseAt) return true;
    return new Date(state.bettingCloseAt).getTime() > Date.now();
});

const placeBet = async (targetId: number) => {
    if (!targetId) return;
    const amount = amounts.value[targetId] ?? 10;
    message.value = null;
    try {
        await trpc.tournament.placeBet.mutate({ targetId, amount });
        message.value = '베팅이 등록되었습니다.';
    } catch (value) {
        message.value = errorText(value);
    } finally {
        await load();
    }
};
</script>

<template>
    <main id="tournament-betting-container" class="betting-page">
        <TournamentPageHeader class="bg0" active-page="betting" title="베 팅 장" />
        <section class="toolbar bg0">
            <button type="button" @click="load">갱신</button>
            <span v-if="loading">불러오는 중...</span>
            <span v-if="message" role="status">{{ message }}</span>
        </section>
        <section v-if="error" class="error bg0" role="alert">{{ error }}</section>
        <section class="state bg0">
            <span>{{ typeNames[snapshot?.state?.type ?? 0] }}</span>
            ({{ stageNames[snapshot?.state?.stage ?? 0] }}, 개막시간 {{ openingTime }}, 경기당
            {{ snapshot?.state?.termSeconds ?? '-' }}초)
        </section>
        <section class="section-title bg2">
            16강 상황<br />
            <small>(전체 금액 : {{ totalAmount }} / 내 투자 금액 : {{ myAmount }})</small>
        </section>

        <TournamentBracket
            class="bg0 betting-bracket"
            :participants="snapshot?.participants ?? []"
            :matches="snapshot?.matches ?? []"
            :winner-id="snapshot?.state?.winnerId"
            :bet-totals="betTotals"
            :my-bet-totals="myBetTotals"
            :total-bet="totalAmount"
            :tournament-type="snapshot?.state?.type ?? 0"
            :show-legend="false"
        />

        <section class="candidate-table bg0">
            <div class="candidate-grid">
                <article v-for="candidate in candidates" :key="candidate.id || candidate.name" class="candidate-card">
                    <GeneralIdentity
                        :name="candidate.name"
                        :picture="candidate.picture"
                        :image-server="candidate.imageServer"
                    />
                    <div class="candidate-return">
                        <span class="ratio-color">{{ ratio(candidate.id) }}</span>
                        <span aria-hidden="true">×</span>
                        <span class="gold-color">{{ amounts[candidate.id] ?? 10 }}</span>
                        <span aria-hidden="true">=</span>
                        <strong class="return-color">{{ expected(candidate.id) }}</strong>
                    </div>
                    <div v-if="bettingOpen" class="candidate-actions">
                        <select
                            v-model.number="amounts[candidate.id]"
                            :aria-label="`${candidate.name} 베팅 금액`"
                            :disabled="!candidate.id"
                        >
                            <option :value="10">금10</option>
                            <option :value="20">금20</option>
                            <option :value="50">금50</option>
                            <option :value="100">금100</option>
                            <option :value="200">금200</option>
                            <option :value="500">금500</option>
                            <option :value="1000">최대</option>
                        </select>
                        <button type="button" :disabled="!candidate.id" @click="placeBet(candidate.id)">베팅</button>
                    </div>
                </article>
            </div>
            <p class="candidate-help">
                <span class="ratio-color">배당률</span> × <span class="gold-color">베팅금</span> =
                <span class="return-color">적중시 환수금</span><br />
                <span class="ratio-color">( 베팅후 500원 이하일땐 베팅이 불가능합니다. )</span>
            </p>
        </section>

        <div class="legacy-table-signature" hidden>
            <table v-for="tableIndex in 6" :key="tableIndex">
                <tbody>
                    <tr v-for="rowIndex in 5" :key="rowIndex">
                        <td></td>
                    </tr>
                </tbody>
            </table>
        </div>

        <section class="ranking-title bg2">토너먼트 랭킹</section>
        <section class="ranking-placeholder bg0">
            순위 / 장수명 / 능력치 / 경기수 / 승리 / 무승부 / 패배 / 집계점수 / 우승횟수
        </section>
        <div class="ranking-tabs bg0" role="tablist" aria-label="토너먼트 랭킹 종목 선택">
            <button
                v-for="section in rankings"
                :key="`ranking-tab-${section.prefix}`"
                type="button"
                role="tab"
                :aria-selected="activeRankingPrefix === section.prefix"
                :class="{ active: activeRankingPrefix === section.prefix }"
                @click="activeRankingPrefix = section.prefix"
            >
                {{ section.title.replaceAll(' ', '') }}
            </button>
        </div>
        <section class="ranking-grid bg0">
            <table
                v-for="section in rankings"
                :key="section.prefix"
                class="ranking-table"
                :class="{ 'mobile-active': activeRankingPrefix === section.prefix }"
            >
                <thead>
                    <tr>
                        <th colspan="9">{{ section.title }}</th>
                    </tr>
                    <tr class="bg1">
                        <th>순</th>
                        <th>장수</th>
                        <th>{{ section.statLabel }}</th>
                        <th>경</th>
                        <th>승</th>
                        <th>무</th>
                        <th>패</th>
                        <th>점</th>
                        <th>勝</th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="entry in section.entries" :key="entry.generalId">
                        <td>{{ entry.rank }}</td>
                        <td class="ranking-general">
                            <GeneralIdentity
                                :name="entry.name"
                                :picture="entry.picture"
                                :image-server="entry.imageServer"
                            />
                        </td>
                        <td>{{ entry.stat }}</td>
                        <td>{{ entry.games }}</td>
                        <td>{{ entry.win }}</td>
                        <td>{{ entry.draw }}</td>
                        <td>{{ entry.lose }}</td>
                        <td>{{ entry.score }}</td>
                        <td>{{ entry.prizes }}</td>
                    </tr>
                    <tr v-if="section.entries.length === 0">
                        <td colspan="9">-</td>
                    </tr>
                </tbody>
            </table>
        </section>
        <section class="guide bg0">
            ㆍ토너먼트의 16강 대진표가 완성되면, 베팅 기간이 주어집니다.<br />
            ㆍ유저들의 베팅 상황에 따라 배당률이 실시간 결정됩니다.<br />
            ㆍ베팅은 16슬롯에 각각 가능하며, 도합 최대 금 1000까지 베팅 가능합니다.<br />
            ㆍ소지금 500원 이하일땐 베팅이 불가능합니다.
        </section>
        <footer class="betting-footer bg0">
            <RouterLink v-slot="{ navigate }" custom to="/">
                <button class="close-button" type="button" @click="navigate">창 닫기</button>
            </RouterLink>
            <small>
                삼국지 모의전투 HiDCHe / KOEI의 이미지를 사용, 응용하였습니다 / 제작 : HideD(hided62@gmail.com) / Credit
            </small>
        </footer>
    </main>
</template>

<style scoped>
.betting-page {
    width: 100%;
    max-width: 1200px;
    min-width: 0;
    min-height: 100vh;
    margin: 0 auto;
    color: #fff;
    font-family: var(--sammo-font-sans);
    font-size: 14px;
    line-height: 1.3;
    text-align: center;
}
.betting-footer {
    padding-top: 20px;
    text-align: left;
}
.betting-footer small {
    display: block;
}
.betting-page,
.betting-page * {
    box-sizing: border-box;
}
.bg0 {
    background: #3a2118 var(--sammo-texture-walnut);
}
.bg2 {
    background: #142b42 var(--sammo-texture-blue);
}
.toolbar {
    min-height: 46px;
    padding: 1px;
    text-align: left;
}
.toolbar button {
    min-width: 72px;
    height: 44px;
    padding: 10px 16px;
    font-size: 14px;
}
.error {
    min-height: 32px;
    padding: 5px;
    text-align: left;
}
.state {
    min-height: 42px;
    padding: 5px;
    font-size: 24px;
}
.state span {
    color: cyan;
}
.section-title {
    min-height: 50px;
    padding: 5px;
    color: limegreen;
    font-size: 24px;
}
.section-title small {
    color: orange;
    font-size: 14px;
}
.candidate-table {
    border: 1px solid gray;
    padding: 10px;
    font-size: 12px;
}
.candidate-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 8px;
}
.candidate-card {
    min-width: 0;
    padding: 8px;
    border: 1px solid #5b504b;
    background: rgb(0 0 0 / 26%);
    text-align: left;
}
.candidate-return {
    display: grid;
    grid-template-columns: 1fr auto 1fr auto 1fr;
    gap: 4px;
    margin: 8px 0;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.candidate-actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 64px;
    gap: 6px;
}
.ratio-color {
    color: skyblue;
}
.expected,
.return-color {
    color: cyan;
}
.gold-color {
    color: orange;
}
select,
.candidate-actions button {
    width: 100%;
    min-height: 27px;
    padding: 2px 1px;
    border: 1px solid #555;
    background: #000;
    color: #fff;
}
button {
    height: 35.5px;
    color: #fff;
    background: #444;
    border: 1px solid #666;
    border-radius: 5.25px;
    cursor: pointer;
}
button:hover,
button:focus {
    filter: brightness(1.25);
}
button:focus-visible,
select:focus-visible {
    outline: 2px solid #f39c12;
    outline-offset: 1px;
}
button:disabled,
select:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}
.candidate-help {
    min-height: 20px;
    margin: 8px 0 0;
    font-size: 18px;
    line-height: 14px;
}
.ranking-title {
    min-height: 50px;
    padding: 8px;
    color: yellow;
    font-size: 24px;
}
.ranking-placeholder {
    min-height: 40px;
    padding: 8px;
    color: skyblue;
}
.ranking-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
    gap: 8px;
    padding: 8px;
}
.ranking-table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
    font-size: 12px;
    line-height: 14px;
}
.ranking-table th,
.ranking-table td {
    height: 28px;
    padding: 1px;
    border: 1px solid #555;
}
.ranking-table tbody tr:has(.general-identity) td {
    height: 66px;
}
.ranking-table thead tr:first-child th {
    height: 18px;
    background: #000;
    font-size: 18px;
    line-height: 18px;
    font-weight: normal;
}
.ranking-table .bg1 {
    background: #213b52;
}
.ranking-table th:nth-child(2),
.ranking-table td:nth-child(2) {
    width: 130px;
    max-width: 130px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ranking-general {
    text-align: left;
}
.ranking-tabs {
    display: none;
}
.guide {
    padding: 10px;
    text-align: left;
}
.error {
    color: #ff8080;
}
@media (max-width: 800px) {
    .betting-page {
        max-width: 100%;
        font-size: 13px;
    }
    .state {
        font-size: 18px;
    }
    .section-title,
    .ranking-title {
        font-size: 20px;
    }
    .candidate-grid {
        grid-template-columns: 1fr;
    }
    .candidate-card {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 112px;
        align-items: center;
        gap: 8px 12px;
    }
    .candidate-return {
        margin: 0;
    }
    .candidate-actions {
        grid-column: 1 / -1;
    }
    .candidate-help {
        font-size: 14px;
        line-height: 18px;
    }
    .ranking-placeholder {
        display: none;
    }
    .ranking-tabs {
        display: grid;
        grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: 5px;
        padding: 8px;
    }
    .ranking-tabs button {
        height: 36px;
        margin: 0;
        border-radius: 3px;
    }
    .ranking-tabs button.active {
        border-color: #f39c12;
        background: #8a5b13;
    }
    .ranking-grid {
        display: block;
        overflow-x: auto;
        padding: 0;
    }
    .ranking-table {
        display: none;
        min-width: 390px;
        font-size: 11px;
    }
    .ranking-table.mobile-active {
        display: table;
    }
    .ranking-table th:nth-child(2),
    .ranking-table td:nth-child(2) {
        width: 112px;
        max-width: 112px;
    }
    .guide,
    .betting-footer {
        padding: 10px;
    }
    .betting-footer small {
        white-space: normal;
    }
}
</style>
