<script lang="ts">
    import type { ExtendedEvent } from "@components/types/event";
    import type { Team } from "@components/types/team";
    import { eventRepo } from "@components/repo/event";
    import { fightRepo } from "@components/repo/fight";
    import { gamemodes, maps } from "@components/stores/stores";
    import { Button } from "@components/ui/button";
    import { Card } from "@components/ui/card";
    import DateTimePicker from "@components/ui/datetime-picker/DateTimePicker.svelte";
    import { Label } from "@components/ui/label";
    import { Select, SelectContent, SelectItem, SelectTrigger } from "@components/ui/select";
    import { Slider } from "@components/ui/slider";
    import { fromAbsolute } from "@internationalized/date";
    import dayjs from "dayjs";
    import { Plus, Shuffle } from "lucide-svelte";
    import { replace } from "svelte-spa-router";

    let { data }: { data: ExtendedEvent } = $props();

    // Seed model (reuse from single elimination)
    interface SeedTeamSlot {
        kind: "TEAM";
        id: number;
    }
    interface SeedGroupSlot {
        kind: "GROUP";
        groupId: number;
        place: number;
    }
    interface SeedFightSlot {
        kind: "FIGHT";
        fightId: number;
        place: 0 | 1;
    }
    type SeedSlot = SeedTeamSlot | SeedGroupSlot | SeedFightSlot;

    let seedSlots = $state<SeedSlot[]>(data.teams.map((t) => ({ kind: "TEAM", id: t.id })));
    const teams = $derived(new Map<number, Team>(data.teams.map((t) => [t.id, t])));
    function shuffleTeams() {
        const teamIndices = seedSlots.map((v, i) => ({ v, i })).filter((x) => x.v.kind === "TEAM");
        const shuffledIds = teamIndices.map((x) => (x.v as SeedTeamSlot).id).sort(() => Math.random() - 0.5);
        let p = 0;
        seedSlots = seedSlots.map((slot) => (slot.kind === "TEAM" ? { kind: "TEAM", id: shuffledIds[p++] } : slot));
    }
    function moveSlot(index: number, dir: -1 | 1) {
        const ni = index + dir;
        if (ni < 0 || ni >= seedSlots.length) return;
        const copy = [...seedSlots];
        const [item] = copy.splice(index, 1);
        copy.splice(ni, 0, item);
        seedSlots = copy;
    }
    function removeSlot(index: number) {
        seedSlots = seedSlots.filter((_, i) => i !== index);
    }
    function addUnknown() {
        seedSlots = [...seedSlots, { kind: "TEAM", id: -1 }];
    }
    let selectedAddTeam = $state<number>(data.teams[0]?.id ?? 0);
    function addTeam() {
        if (selectedAddTeam !== undefined) seedSlots = [...seedSlots, { kind: "TEAM", id: selectedAddTeam }];
    }
    let selectedGroup = $state<number | null>(data.groups[0]?.id ?? null);
    let selectedGroupPlace = $state<number>(0);
    function addGroupPlace() {
        if (selectedGroup != null) seedSlots = [...seedSlots, { kind: "GROUP", groupId: selectedGroup, place: selectedGroupPlace }];
    }
    let selectedFight = $state<number | null>(data.fights[0]?.id ?? null);
    let selectedFightPlace = $state<0 | 1>(0);
    function addFightPlace() {
        if (selectedFight != null) seedSlots = [...seedSlots, { kind: "FIGHT", fightId: selectedFight, place: selectedFightPlace }];
    }

    // Config
    let startTime = $state(fromAbsolute(data.event.start, "Europe/Berlin"));
    let roundTime = $state(30);
    let startDelay = $state(30);
    let gamemode = $state("");
    let map = $state("");
    let selectableGamemodes = $derived($gamemodes.map((g) => ({ name: g, value: g })).sort((a, b) => a.name.localeCompare(b.name)));
    let mapsStore = $derived(maps(gamemode));
    let selectableMaps = $derived($mapsStore.map((m) => ({ name: m, value: m })).sort((a, b) => a.name.localeCompare(b.name)));

    // Build winners bracket rounds (same as single elimination seeding)
    interface BracketFightPreview {
        blue: SeedSlot;
        red: SeedSlot;
    }
    type BracketRoundPreview = BracketFightPreview[];
    function buildWinnersRounds(order: SeedSlot[]): BracketRoundPreview[] {
        const n = order.length;
        if (n < 2) return [];
        if ((n & (n - 1)) !== 0) return []; // power of two required
        const rounds: BracketRoundPreview[] = [];
        let round: BracketRoundPreview = [];
        for (let i = 0; i < order.length; i += 2) round.push({ blue: order[i], red: order[i + 1] });
        rounds.push(round);
        let prevWinners = round.map((f) => f.blue);
        while (prevWinners.length > 1) {
            const next: BracketRoundPreview = [];
            for (let i = 0; i < prevWinners.length; i += 2) next.push({ blue: prevWinners[i], red: prevWinners[i + 1] });
            rounds.push(next);
            prevWinners = next.map((f) => f.blue);
        }
        return rounds;
    }
    let winnersRounds = $derived(buildWinnersRounds(seedSlots));

    // Losers bracket structure: each losers round takes losers from previous winners round or previous losers round.
    // Simplified pairing: For each winners round except final, collect losers and pair them sequentially; then advance until one remains for losers final.
    function buildLosersTemplate(wRounds: BracketRoundPreview[]): BracketRoundPreview[] {
        const losersRounds: BracketRoundPreview[] = [];
        if (wRounds.length < 2) return losersRounds;
        // Round 1 losers (from winners round 1)
        const firstLosersPairs: BracketRoundPreview = [];
        wRounds[0].forEach((f) => firstLosersPairs.push({ blue: f.blue, red: f.red })); // placeholders (will label as losers)
        losersRounds.push(firstLosersPairs);
        // Subsequent losers rounds shrink similarly
        let remaining = firstLosersPairs.length; // number of fights that feed losers next stage
        while (remaining > 1) {
            const next: BracketRoundPreview = [];
            for (let i = 0; i < remaining; i += 2) next.push(firstLosersPairs[i]); // placeholder reuse
            losersRounds.push(next);
            remaining = next.length;
        }
        return losersRounds;
    }
    let losersRounds = $derived(buildLosersTemplate(winnersRounds));

    let generateDisabled = $derived(gamemode !== "" && map !== "" && winnersRounds.length > 0 && seedSlots.length >= 4);

    // Type helpers
    function slotLabel(slot: SeedSlot): string {
        if (slot.kind === "TEAM") return teams.get(slot.id)?.name ?? "???";
        if (slot.kind === "GROUP") {
            const gname = data.groups.find((g) => g.id === slot.groupId)?.name ?? "?";
            return `(Grp ${gname} Platz ${slot.place + 1})`;
        }
        if (slot.kind === "FIGHT") {
            const f = data.fights.find((x) => x.id === slot.fightId);
            const when = f ? new Date(f.start).toLocaleTimeString("de-DE", { timeStyle: "short" }) : "?";
            const vs = f ? `${f.blueTeam.kuerzel} vs. ${f.redTeam.kuerzel}` : "Kampf";
            return `${slot.place === 0 ? "Gewinner" : "Verlierer"} von ${vs} (${when})`;
        }
        return "???";
    }

    async function generateDouble() {
        if (!generateDisabled) return;
        const eventId = data.event.id;
        // Create two groups: winners & losers + grand final group (optional combine winners)
        const winnersGroup = await $eventRepo.createGroup(eventId, { name: "Winners", type: "ELIMINATION_STAGE" });
        const losersGroup = await $eventRepo.createGroup(eventId, { name: "Losers", type: "ELIMINATION_STAGE" });
        const finalGroup = await $eventRepo.createGroup(eventId, { name: "Final", type: "ELIMINATION_STAGE" });

        function fallbackTeamId(slot: SeedSlot): number {
            if (slot.kind === "GROUP" || slot.kind === "FIGHT") return -1;
            if (slot.kind === "TEAM") return slot.id;
            return data.teams[0].id;
        }

        const winnersFightIdsByRound: number[][] = [];
        for (let r = 0; r < winnersRounds.length; r++) {
            const round = winnersRounds[r];
            const ids: number[] = [];
            for (let i = 0; i < round.length; i++) {
                let finalMap = map;
                if (finalMap === "%random%" && selectableMaps.length > 0) finalMap = selectableMaps[Math.floor(Math.random() * selectableMaps.length)].value;
                const f = await $fightRepo.createFight(eventId, {
                    blueTeam: fallbackTeamId(round[i].blue),
                    redTeam: fallbackTeamId(round[i].red),
                    group: winnersGroup.id,
                    map: finalMap,
                    spectatePort: null,
                    spielmodus: gamemode,
                    start: dayjs(
                        startTime
                            .copy()
                            .add({ minutes: roundTime * r })
                            .add({ seconds: startDelay * i })
                            .toDate()
                    ),
                });
                // Attach relations for GROUP/FIGHT seeds
                const pair = round[i];
                if (pair.blue.kind === "GROUP") await $eventRepo.createRelation(eventId, { fightId: f.id, team: "BLUE", fromType: "GROUP", fromId: pair.blue.groupId, fromPlace: pair.blue.place });
                if (pair.red.kind === "GROUP") await $eventRepo.createRelation(eventId, { fightId: f.id, team: "RED", fromType: "GROUP", fromId: pair.red.groupId, fromPlace: pair.red.place });
                if (pair.blue.kind === "FIGHT") await $eventRepo.createRelation(eventId, { fightId: f.id, team: "BLUE", fromType: "FIGHT", fromId: pair.blue.fightId, fromPlace: pair.blue.place });
                if (pair.red.kind === "FIGHT") await $eventRepo.createRelation(eventId, { fightId: f.id, team: "RED", fromType: "FIGHT", fromId: pair.red.fightId, fromPlace: pair.red.place });
                ids.push(f.id);
            }
            winnersFightIdsByRound.push(ids);
        }

        // Progression in winners bracket
        for (let r = 1; r < winnersFightIdsByRound.length; r++) {
            const prev = winnersFightIdsByRound[r - 1];
            const curr = winnersFightIdsByRound[r];
            for (let i = 0; i < curr.length; i++) {
                const target = curr[i];
                const srcA = prev[i * 2];
                const srcB = prev[i * 2 + 1];
                await $eventRepo.createRelation(eventId, { fightId: target, team: "BLUE", fromType: "FIGHT", fromId: srcA, fromPlace: 0 });
                await $eventRepo.createRelation(eventId, { fightId: target, team: "RED", fromType: "FIGHT", fromId: srcB, fromPlace: 0 });
            }
        }

        // Losers bracket (canonical pattern):
        // L1: losers of WBR1 paired; then for r=2..(k-1):
        //   Major: winners of previous L vs losers of WBRr
        //   Minor: winners of that major paired (except after last WBR where we go to LB final vs WB final loser)
        const losersFightIdsByRound: number[][] = [];
        let losersRoundIndex = 0;
        const k = winnersFightIdsByRound.length; // number of winners rounds

        // L1 from WBR1 losers
        {
            const wb1 = winnersFightIdsByRound[0];
            const ids: number[] = [];
            for (let i = 0; i < wb1.length; i += 2) {
                let finalMap = map;
                if (finalMap === "%random%" && selectableMaps.length > 0) finalMap = selectableMaps[Math.floor(Math.random() * selectableMaps.length)].value;
                const lf = await $fightRepo.createFight(eventId, {
                    blueTeam: -1,
                    redTeam: -1,
                    group: losersGroup.id,
                    map: finalMap,
                    spectatePort: null,
                    spielmodus: gamemode,
                    start: dayjs(
                        startTime
                            .copy()
                            .add({ minutes: roundTime * (k + losersRoundIndex) })
                            .add({ seconds: startDelay * (i / 2) })
                            .toDate()
                    ),
                });
                await $eventRepo.createRelation(eventId, { fightId: lf.id, team: "BLUE", fromType: "FIGHT", fromId: wb1[i], fromPlace: 1 });
                await $eventRepo.createRelation(eventId, { fightId: lf.id, team: "RED", fromType: "FIGHT", fromId: wb1[i + 1], fromPlace: 1 });
                ids.push(lf.id);
            }
            losersFightIdsByRound.push(ids);
            losersRoundIndex++;
        }

        // For each subsequent winners round except the final
        for (let wr = 1; wr < k - 1; wr++) {
            const prevLBRound = losersFightIdsByRound[losersFightIdsByRound.length - 1];

            // Major: winners of prevLBRound vs losers of current WBR (wr)
            {
                const ids: number[] = [];
                for (let j = 0; j < winnersFightIdsByRound[wr].length; j++) {
                    let finalMap = map;
                    if (finalMap === "%random%" && selectableMaps.length > 0) finalMap = selectableMaps[Math.floor(Math.random() * selectableMaps.length)].value;
                    const lf = await $fightRepo.createFight(eventId, {
                        blueTeam: -1,
                        redTeam: -1,
                        group: losersGroup.id,
                        map: finalMap,
                        spectatePort: null,
                        spielmodus: gamemode,
                        start: dayjs(
                            startTime
                                .copy()
                                .add({ minutes: roundTime * (k + losersRoundIndex) })
                                .add({ seconds: startDelay * j })
                                .toDate()
                        ),
                    });
                    await $eventRepo.createRelation(eventId, { fightId: lf.id, team: "BLUE", fromType: "FIGHT", fromId: prevLBRound[j], fromPlace: 0 });
                    await $eventRepo.createRelation(eventId, { fightId: lf.id, team: "RED", fromType: "FIGHT", fromId: winnersFightIdsByRound[wr][j], fromPlace: 1 });
                    ids.push(lf.id);
                }
                losersFightIdsByRound.push(ids);
                losersRoundIndex++;
            }

            // Minor: pair winners of last LBRound among themselves (if more than 1)
            {
                const last = losersFightIdsByRound[losersFightIdsByRound.length - 1];
                if (last.length > 1) {
                    const ids: number[] = [];
                    for (let j = 0; j < last.length; j += 2) {
                        let finalMap = map;
                        if (finalMap === "%random%" && selectableMaps.length > 0) finalMap = selectableMaps[Math.floor(Math.random() * selectableMaps.length)].value;
                        const lf = await $fightRepo.createFight(eventId, {
                            blueTeam: -1,
                            redTeam: -1,
                            group: losersGroup.id,
                            map: finalMap,
                            spectatePort: null,
                            spielmodus: gamemode,
                            start: dayjs(
                                startTime
                                    .copy()
                                    .add({ minutes: roundTime * (k + losersRoundIndex) })
                                    .add({ seconds: startDelay * (j / 2) })
                                    .toDate()
                            ),
                        });
                        await $eventRepo.createRelation(eventId, { fightId: lf.id, team: "BLUE", fromType: "FIGHT", fromId: last[j], fromPlace: 0 });
                        await $eventRepo.createRelation(eventId, { fightId: lf.id, team: "RED", fromType: "FIGHT", fromId: last[j + 1], fromPlace: 0 });
                        ids.push(lf.id);
                    }
                    losersFightIdsByRound.push(ids);
                    losersRoundIndex++;
                }
            }
        }

        // Final losers round: winners of last LBRound vs loser of Winners Final (last WBR)
        const winnersFinal = winnersFightIdsByRound[k - 1][0];
        const lastLBRound = losersFightIdsByRound[losersFightIdsByRound.length - 1];
        let losersFinal: number | undefined = undefined;
        if (lastLBRound && lastLBRound.length >= 1) {
            let finalMap2 = map;
            if (finalMap2 === "%random%" && selectableMaps.length > 0) finalMap2 = selectableMaps[Math.floor(Math.random() * selectableMaps.length)].value;
            const lf = await $fightRepo.createFight(eventId, {
                blueTeam: -1,
                redTeam: -1,
                group: losersGroup.id,
                map: finalMap2,
                spectatePort: null,
                spielmodus: gamemode,
                start: dayjs(
                    startTime
                        .copy()
                        .add({ minutes: roundTime * (k + losersRoundIndex) })
                        .toDate()
                ),
            });
            await $eventRepo.createRelation(eventId, { fightId: lf.id, team: "BLUE", fromType: "FIGHT", fromId: lastLBRound[lastLBRound.length - 1], fromPlace: 0 });
            await $eventRepo.createRelation(eventId, { fightId: lf.id, team: "RED", fromType: "FIGHT", fromId: winnersFinal, fromPlace: 1 });
            losersFinal = lf.id;
            losersFightIdsByRound.push([lf.id]);
            losersRoundIndex++;
        }

        // Grand Final
        let finalMap = map;
        if (finalMap === "%random%" && selectableMaps.length > 0) finalMap = selectableMaps[Math.floor(Math.random() * selectableMaps.length)].value;
        const grandFinal = await $fightRepo.createFight(eventId, {
            blueTeam: -1,
            redTeam: -1,
            group: finalGroup.id,
            map: finalMap,
            spectatePort: null,
            spielmodus: gamemode,
            start: dayjs(
                startTime
                    .copy()
                    .add({ minutes: roundTime * (k + losersRoundIndex) })
                    .toDate()
            ),
        });
        await $eventRepo.createRelation(eventId, { fightId: grandFinal.id, team: "BLUE", fromType: "FIGHT", fromId: winnersFinal, fromPlace: 0 });
        if (losersFinal !== undefined) await $eventRepo.createRelation(eventId, { fightId: grandFinal.id, team: "RED", fromType: "FIGHT", fromId: losersFinal, fromPlace: 0 });

        await replace("#/event/" + eventId);
    }
</script>

<Card class="p-4 mb-4">
    <div class="flex justify-between items-center mb-4">
        <h2 class="text-xl font-bold">Double Elimination Bracket</h2>
        <div class="flex gap-2">
            <Button onclick={shuffleTeams} aria-label="Shuffle Teams"><Shuffle size={16} /> Shuffle</Button>
        </div>
    </div>
    {#if seedSlots.length < 4}
        <p class="text-gray-400">Mindestens vier Seeds benötigt.</p>
    {:else if winnersRounds.length === 0}
        <p class="text-yellow-400">Seedanzahl muss eine Zweierpotenz sein. Aktuell: {seedSlots.length}</p>
    {/if}
    <div class="grid lg:grid-cols-3 gap-6">
        <div class="space-y-4">
            <Label>Seeds</Label>
            <ul class="mt-2 space-y-1">
                {#each seedSlots as slot, i (i)}
                    <li class="flex items-center gap-2 text-sm">
                        <span class="w-6 text-right">{i + 1}.</span>
                        <span class="flex-1 truncate">{slotLabel(slot)}</span>
                        <div class="flex gap-1">
                            <Button size="sm" onclick={() => moveSlot(i, -1)} disabled={i === 0}>↑</Button>
                            <Button size="sm" onclick={() => moveSlot(i, 1)} disabled={i === seedSlots.length - 1}>↓</Button>
                            <Button size="sm" variant="destructive" onclick={() => removeSlot(i)}>✕</Button>
                        </div>
                    </li>
                {/each}
            </ul>
            <div class="space-y-2">
                <Label>Hinzufügen</Label>
                <div class="flex flex-col gap-2">
                    <div class="flex flex-wrap gap-2 items-center">
                        <select class="bg-gray-800 border border-gray-700 rounded px-2 py-1" bind:value={selectedAddTeam}>
                            {#each data.teams as t}<option value={t.id}>{t.name}</option>{/each}
                        </select>
                        <Button size="sm" onclick={addTeam}>Team</Button>
                        <Button size="sm" onclick={addUnknown}>???</Button>
                    </div>
                    {#if data.groups.length > 0}
                        <div class="flex flex-wrap gap-2 items-center">
                            <select class="bg-gray-800 border border-gray-700 rounded px-2 py-1" bind:value={selectedGroup}>
                                {#each data.groups as g}<option value={g.id}>{g.name}</option>{/each}
                            </select>
                            <select class="bg-gray-800 border border-gray-700 rounded px-2 py-1" bind:value={selectedGroupPlace}>
                                {#each Array(16) as _, idx}<option value={idx}>{idx + 1}. Platz</option>{/each}
                            </select>
                            <Button size="sm" onclick={addGroupPlace}>Gruppenplatz</Button>
                        </div>
                    {/if}
                    {#if data.fights.length > 0}
                        <div class="flex flex-wrap gap-2 items-center">
                            <select class="bg-gray-800 border border-gray-700 rounded px-2 py-1" bind:value={selectedFight}>
                                {#each data.fights as f}
                                    <option value={f.id}>{new Date(f.start).toLocaleTimeString("de-DE", { timeStyle: "short" })}: {f.blueTeam.kuerzel} vs. {f.redTeam.kuerzel}</option>
                                {/each}
                            </select>
                            <select class="bg-gray-800 border border-gray-700 rounded px-2 py-1" bind:value={selectedFightPlace}>
                                <option value={0}>Gewinner</option>
                                <option value={1}>Verlierer</option>
                            </select>
                            <Button size="sm" onclick={addFightPlace}>Kampfplatz</Button>
                        </div>
                    {/if}
                </div>
            </div>
        </div>
        <div>
            <Label>Konfiguration</Label>
            <DateTimePicker bind:value={startTime} />
            <div class="mt-4">
                <Label>Rundenzeit: {roundTime}m</Label>
                <Slider type="single" bind:value={roundTime} step={5} min={5} max={60} />
            </div>
            <div class="mt-4">
                <Label>Startverzögerung: {startDelay}s</Label>
                <Slider type="single" bind:value={startDelay} step={5} min={0} max={60} />
            </div>
            <div class="mt-4">
                <Label>Spielmodus</Label>
                <Select type="single" bind:value={gamemode}>
                    <SelectTrigger>{gamemode}</SelectTrigger>
                    <SelectContent>
                        {#each selectableGamemodes as gm}<SelectItem value={gm.value}>{gm.name}</SelectItem>{/each}
                    </SelectContent>
                </Select>
            </div>
            <div class="mt-4">
                <Label>Map</Label>
                <Select type="single" bind:value={map}>
                    <SelectTrigger>{map}</SelectTrigger>
                    <SelectContent>
                        <SelectItem value="%random%">Zufällige Map</SelectItem>
                        {#each selectableMaps as mp}<SelectItem value={mp.value}>{mp.name}</SelectItem>{/each}
                    </SelectContent>
                </Select>
            </div>
        </div>
        <div class="space-y-6">
            <div>
                <Label>Winners Bracket Vorschau</Label>
                {#if winnersRounds.length > 0}
                    <div class="flex gap-6 overflow-x-auto mt-2">
                        {#each winnersRounds as round, r}
                            <div>
                                <h3 class="font-semibold mb-2">W Runde {r + 1}</h3>
                                <ul class="space-y-1">
                                    {#each round as fight, i}
                                        <li class="p-2 border border-gray-700 rounded text-xs">
                                            <span class="text-gray-400"
                                                >{new Intl.DateTimeFormat("de-DE", { hour: "2-digit", minute: "2-digit" }).format(
                                                    startTime
                                                        .copy()
                                                        .add({ minutes: roundTime * r, seconds: startDelay * i })
                                                        .toDate()
                                                )}</span
                                            >
                                            : {slotLabel(fight.blue)} vs. {slotLabel(fight.red)}
                                        </li>
                                    {/each}
                                </ul>
                            </div>
                        {/each}
                    </div>
                {/if}
            </div>
            <div>
                <Label>Losers Bracket (vereinfachte Vorschau)</Label>
                {#if losersRounds.length > 0}
                    <div class="flex gap-6 overflow-x-auto mt-2">
                        {#each losersRounds as round, r}
                            <div>
                                <h3 class="font-semibold mb-2">L Runde {r + 1}</h3>
                                <ul class="space-y-1">
                                    {#each round as fight, i}
                                        <li class="p-2 border border-gray-700 rounded text-xs">
                                            Verlierer Paar {i + 1} (aus W Runde {r + 1})
                                        </li>
                                    {/each}
                                </ul>
                            </div>
                        {/each}
                    </div>
                {/if}
            </div>
        </div>
    </div>
    <Button class="!p-4 fixed bottom-4 right-4" disabled={!generateDisabled} onclick={generateDouble} aria-label="Double Bracket generieren">
        <Plus />
    </Button>
</Card>

<!-- minimal styles only -->
