<!--
  -   This file is a part of the SteamWar software.
  -
  -   Copyright (C) 2023 SteamWar.de-Serverteam
  -
  -   This program is free software: you can redistribute it and/or modify
  -   it under the terms of the GNU Affero General Public License as published by
  -   the Free Software Foundation, either version 3 of the License, or
  -   (at your option) any later version.
  -
  -   This program is distributed in the hope that it will be useful,
  -   but WITHOUT ANY WARRANTY; without even the implied warranty of
  -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  -   GNU Affero General Public License for more details.
  -
  -   You should have received a copy of the GNU Affero General Public License
  -   along with this program.  If not, see <https://www.gnu.org/licenses/>.
  -->

<script lang="ts">
    import type {ExtendedEvent} from "@type/event.ts";
    import TeamChip from "./TeamChip.svelte";
    import type {Team} from "@type/team.ts";
    import DragAcceptor from "./DragAcceptor.svelte";
    import {Button, Input, Label, Modal, Range, Select} from "flowbite-svelte";
    import {gamemodes, maps} from "@stores/stores.ts";
    import {PlusSolid} from "flowbite-svelte-icons";
    import {replace} from "svelte-spa-router";
    import dayjs from "dayjs";
    import {fightRepo} from "@repo/fight.ts";

    export let data: ExtendedEvent;
    $: teams = new Map<number, Team>(data.teams.map(team => [team.id, team]));

    let groups: number[][] = [];
    $: teamsNotInGroup = data.teams.filter(team => !groups.flat().includes(team.id));

    function dragToNewGroup(event: CustomEvent<DragEvent>) {
        event.detail.preventDefault();
        let teamId = parseInt(event.detail.dataTransfer!.getData("team"));
        groups = [...groups.map(value => value.filter(value1 => value1 != teamId)), [teamId]].filter(value => value.length > 0);
    }

    function teamDragStart(ev: DragEvent, team: Team) {
        ev.dataTransfer!.setData("team", team.id.toString());
    }

    let resetDragOver = false;

    function resetDragOverEvent(ev: DragEvent) {
        resetDragOver = true;
        ev.preventDefault();
    }

    function dropReset(ev: DragEvent) {
        ev.preventDefault();
        let teamId = parseInt(ev.dataTransfer!.getData("team"));
        groups = groups.map(group => group.filter(team => team !== teamId)).filter(group => group.length > 0);
        resetDragOver = false;
    }

    function dropGroup(ev: CustomEvent<DragEvent>, groupIndex: number) {
        ev.preventDefault();
        let teamId = parseInt(ev.detail.dataTransfer!.getData("team"));
        groups = groups.map((group, i) => i === groupIndex ? [...group.filter(value => value != teamId), teamId] : group.filter(value => value != teamId)).filter(group => group.length > 0);
    }

    let startTime = dayjs(data.event.start).utc(true).toISOString().slice(0, -1);
    $: startMoment = dayjs(startTime);
    let gamemode = "";
    let map = "";

    $: selectableGamemodes = $gamemodes.map(gamemode => {
        return {
            name: gamemode,
            value: gamemode
        };
    }).sort((a, b) => a.name.localeCompare(b.name));

    $: mapsStore = maps(gamemode);
    $: selectableMaps = $mapsStore.map(map => {
        return {
            name: map,
            value: map
        };
    }).sort((a, b) => a.name.localeCompare(b.name));

    let roundTime = 30;
    let startDelay = 30;

    let showAutoGrouping = false;
    let groupCount = Math.floor(data.teams.length / 2);

    function createGroups() {
        let teams = data.teams.map(team => team.id).sort(() => Math.random() - 0.5);
        groups = [];
        for (let i = 0; i < groupCount; i++) {
            groups.push([]);
        }
        while (teams.length > 0) {
            groups[teams.length % groupCount].push(teams.pop() as number);
        }
        showAutoGrouping = false;
        groups = groups.filter(group => group.length > 0);
    }

    function generateGroups(groups: number[][]): number[][][][] {
        const groupFights: number[][][][] = [];
        groups.forEach((group) => {
            let round = group.length + (group.length % 2) - 1;
            let groupFight = [];
            for (let i = 0; i < round; i++) {
                let availableTeams = [...group];
                if (group.length % 2 === 1) {
                    availableTeams = availableTeams.filter((team, index) => index !== i);
                }
                let roundFights = [];
                while (availableTeams.length > 0) {
                    let team1 = availableTeams.pop() as number;
                    let team2 = availableTeams.at(i % availableTeams.length) as number;
                    availableTeams = availableTeams.filter(team => team !== team2);
                    let fight = [team1, team2];
                    fight.sort(() => Math.random() - 0.5);
                    roundFights.push(fight);
                }
                groupFight.push(roundFights);
            }
            groupFights.push(groupFight);
        });
        return groupFights;
    }

    $: groupsFights = generateGroups(groups);

    $: generateDisabled = groupsFights.length > 0 && groupsFights.every(value => value.every(value1 => value1.length > 0)) && gamemode !== "" && map !== "";

    async function generateFights() {
        groupsFights.forEach((group, i) => {
            group.forEach((round, j) => {
                round.forEach(async (fight, k) => {
                    const blueTeam = teams.get(fight[0])!;
                    const redTeam = teams.get(fight[1])!;

                    await $fightRepo.createFight(data.event.id, {
                        blueTeam: blueTeam.id,
                        redTeam: redTeam.id,
                        group: "Gruppe " + (i + 1),
                        kampfleiter: 0,
                        map: map,
                        spielmodus: gamemode,
                        start: startMoment.clone().add(roundTime * j, "minutes").add(startDelay * (k + (i * round.length)), "seconds")
                    });
                });
            });
        });

        await replace("#/event/" + data.event.id);
    }
</script>

<div class="flex justify-between">
    <div id="reseter" class:border-white={resetDragOver}
         class="flex m-2 bg-gray-800 w-fit p-2 border border-gray-700 rounded ml-4 h-20 pt-6 relative"
         on:dragover={resetDragOverEvent} on:dragleave={() => resetDragOver = false} on:drop={dropReset} role="group">
        {#each teamsNotInGroup as team (team.id)}
            <TeamChip {team} on:dragstart={ev => teamDragStart(ev, team)}/>
        {/each}
    </div>

    <div class="flex items-center mr-4">
        <Button on:click={() => showAutoGrouping = true}>Automatic Grouping</Button>
    </div>
</div>

<div class="flex m-4 gap-4 border-b border-gray-700 pb-4">
    {#each groups as group, i (i)}
        <DragAcceptor on:drop={ev => dropGroup(ev, i)}>
            <h1>Group {i + 1} ({group.length})</h1>
            {#each group as teamId (teamId)}
                <TeamChip team={teams.get(teamId)} on:dragstart={ev => teamDragStart(ev, teams.get(teamId))}/>
            {/each}
        </DragAcceptor>
    {/each}
    <DragAcceptor on:drop={dragToNewGroup}>
        <h1>Create Group</h1>
    </DragAcceptor>
</div>

<div class="m-4 border-b border-gray-700 pb-4">
    <Label for="event-end">Start Time</Label>
    <Input id="event-end" bind:value={startTime} class="w-80" let:props size="lg">
        <input type="datetime-local" {...props} bind:value={startTime}/>
    </Input>
    <div class="mt-2">
        <Label for="event-roundtime">Round time: {roundTime}m</Label>
        <Range id="event-roundtime" bind:value={roundTime} step="1" min="5" max="60"/>
    </div>
    <div class="mt-2">
        <Label for="event-member">Start delay: {startDelay}</Label>
        <Range id="event-member" bind:value={startDelay} step="1" min="0" max="30"/>
    </div>
    <div class="mt-2">
        <Label for="fight-gamemode">Gamemode</Label>
        <Select items={selectableGamemodes} bind:value={gamemode} id="fight-gamemode"></Select>
    </div>
    <div class="mt-2">
        <Label for="fight-maps">Map</Label>
        <Select items={selectableMaps} bind:value={map} id="fight-maps"></Select>
    </div>
</div>

<div class="text-center mx-2">
    {#each groupsFights as fightsGroup, i}
        <div>
            <h1 class="text-4xl">Group: {i + 1}</h1>
            {#each fightsGroup as fightsRound, j}
                <div class="border-b border-gray-700">
                    <h1 class="text-2xl">Round: {j + 1}</h1>
                    {#each fightsRound as fightTeams, k}
                        <div class="text-left p-4">
                            <span class="bg-gray-800 p-2 border border-gray-700 rounded">{startMoment.clone().add(roundTime * j, "minutes").add(startDelay * (k + (i * fightsRound.length)), "seconds").format("DD.MM.yyyy HH:mm:ss")}</span>
                            {teams.get(fightTeams[0]).name} vs. {teams.get(fightTeams[1]).name}
                        </div>
                    {/each}
                </div>
            {/each}
        </div>
    {/each}
</div>

<Button class="!p-4 fixed bottom-4 right-4" pill disabled={!generateDisabled} on:click={generateFights}>
    <PlusSolid/>
</Button>

<Modal bind:open={showAutoGrouping} outsideclose title="Auto Grouping" size="sm">
    <Label for="event-member">Groups: {groupCount}</Label>
    <Range id="event-member" bind:value={groupCount} step="1" min="1" max={Math.floor(data.teams.length / 2)}/>

    <svelte:fragment slot="footer">
        <Button class="ml-auto" on:click={createGroups}>Create</Button>
        <Button color="alternative" on:click={() => showAutoGrouping = false}>Cancel</Button>
    </svelte:fragment>
</Modal>

<style lang="scss">
  #reseter::before {
    content: 'Reset';
    position: absolute;
    top: 0;
    color: gray;
  }

  #reseter {
    min-width: 14rem;
  }
</style>


