<script lang="ts">
    import { onMount, onDestroy } from "svelte";
    import type { IViewer } from "./viewers/IViewer";
    import { createViewer } from "./viewers/ViewerFactory";
    import { Cube, WatsonHealth3DPrintMesh } from "carbon-icons-svelte";

    interface Data {
        input: string;
        input_path: string;
        model1: string;
        model1_path: string;
        model2: string;
        model2_path: string;
    }

    let viewerA: IViewer;
    let viewerB: IViewer;
    let canvasA: HTMLCanvasElement;
    let canvasB: HTMLCanvasElement;
    let containerA: HTMLDivElement;
    let containerB: HTMLDivElement;
    let overlayA: HTMLDivElement;
    let overlayB: HTMLDivElement;
    let normalToggleA: HTMLInputElement;
    let normalToggleB: HTMLInputElement;
    let wireframeToggleA: HTMLInputElement;
    let wireframeToggleB: HTMLInputElement;
    let loadingBarFillA: HTMLDivElement;
    let loadingBarFillB: HTMLDivElement;
    let voteOverlay: boolean = false;
    let voteOverlayA: HTMLDivElement;
    let voteOverlayB: HTMLDivElement;
    let statusMessage: string = "Loading...";
    let errorMessage: string = "";
    let data: Data;

    async function fetchScenes() {
        statusMessage = "Loading...";
        errorMessage = "";

        try {
            const url = "/api/fetchScenes";
            const token = localStorage.getItem("access_token");
            const response = await fetch(url, {
                method: "GET",
                headers: {
                    "Cache-Control": "no-cache",
                    Authorization: `Bearer ${token}`,
                },
            });
            const result = await response.json();
            if (result.input) {
                data = result;
                statusMessage = "";
                return true;
            } else {
                statusMessage = "Voting complete.";
                return false;
            }
        } catch (error) {
            errorMessage = "Failed to fetch pair.";
            statusMessage = "";
            return false;
        }
    }

    async function loadScenes() {
        viewerA?.dispose();
        viewerB?.dispose();

        const success = await fetchScenes();
        if (!success) return;

        overlayA.style.display = "flex";
        overlayB.style.display = "flex";

        const baseUrl = "https://huggingface.co/datasets/dylanebert/3d-arena/resolve/main/";
        const model1_path = `${baseUrl}${data.model1_path}`;
        const model2_path = `${baseUrl}${data.model2_path}`;

        try {
            [viewerA, viewerB] = await Promise.all([
                createViewer(model1_path, canvasA, (progress) => {
                    loadingBarFillA.style.width = `${progress * 100}%`;
                }),
                createViewer(model2_path, canvasB, (progress) => {
                    loadingBarFillB.style.width = `${progress * 100}%`;
                }),
            ]);

            window.addEventListener("resize", handleResize);
            handleResize();
        } catch (error) {
            console.log(error);
            errorMessage = "Failed to load scenes.";
        }

        overlayA.style.display = "none";
        overlayB.style.display = "none";
    }

    async function vote(option: "A" | "B") {
        voteOverlay = true;
        voteOverlayA.classList.add("show");
        voteOverlayB.classList.add("show");

        const token = localStorage.getItem("access_token");
        if (!token) {
            window.location.href = "/api/authorize";
            return;
        }

        const payload = {
            input: data.input,
            better: option == "A" ? data.model1 : data.model2,
            worse: option == "A" ? data.model2 : data.model1,
        };
        const url = `/api/vote`;

        const startTime = Date.now();

        try {
            const response = await fetch(url, {
                method: "POST",
                headers: {
                    "Cache-Control": "no-cache",
                    "Content-Type": "application/json",
                    Authorization: `Bearer ${token}`,
                },
                body: JSON.stringify(payload),
            });

            const elapsedTime = Date.now() - startTime;
            const remainingTime = Math.max(1200 - elapsedTime, 0);

            if (response.ok) {
                const result = await response.json();
                console.log(result);
                setTimeout(() => {
                    voteOverlayA.classList.remove("show");
                    voteOverlayB.classList.remove("show");
                    voteOverlay = false;
                    loadScenes();
                }, remainingTime);
            } else {
                if (response.status === 401) {
                    statusMessage = "Unauthorized. Redirecting to login...";
                    await new Promise((resolve) => setTimeout(resolve, 1000));
                    window.location.href = "/api/authorize";
                } else {
                    errorMessage = "Failed to process vote.";
                }
            }
        } catch (error) {
            errorMessage = "Failed to process vote.";
            statusMessage = "";
        }
    }

    function skip() {
        loadScenes();
    }

    function handleResize() {
        requestAnimationFrame(() => {
            if (canvasA && containerA) {
                canvasA.width = containerA.clientWidth;
                canvasA.height = containerA.clientHeight;
            }
            if (canvasB && containerB) {
                canvasB.width = containerB.clientWidth;
                canvasB.height = containerB.clientHeight;
            }
        });
    }

    function setRenderMode(viewer: IViewer, mode: string) {
        viewer.setRenderMode(mode);
    }

    onMount(loadScenes);

    onDestroy(() => {
        viewerA?.dispose();
        viewerB?.dispose();
        if (typeof window !== "undefined") {
            window.removeEventListener("resize", handleResize);
        }
    });

    $: if (data && data.input_path) {
        console.log("Input path changed:", data.input_path);
    }
</script>

{#if errorMessage}
    <p class="center-title muted" style="color: red;">{errorMessage}</p>
{:else if statusMessage}
    <p class="center-title muted">{statusMessage}</p>
{:else}
    <div class="vote-input">
        <img
            src={`https://huggingface.co/datasets/dylanebert/3d-arena/resolve/main/${data.input_path}`}
            class="input-image"
            alt="Input"
        />
    </div>
    <h2 class="center-title">Which is better?</h2>
    <p class="center-subtitle">Use mouse/touch to change the view.</p>
    <div class="voting-container">
        <div bind:this={containerA} class="canvas-container">
            <div bind:this={overlayA} class="loading-overlay">
                <div class="loading-bar">
                    <div bind:this={loadingBarFillA} class="loading-bar-fill" />
                </div>
            </div>
            <div bind:this={voteOverlayA} class="vote-overlay">{data.model1}</div>
            <canvas bind:this={canvasA} class="viewer-canvas" id="canvas1"> </canvas>
            <div class="stats">
                {#if viewerA}
                    <p>vertex count: {viewerA.vertexCount}</p>
                {/if}
            </div>
            {#if viewerA && !viewerA.topoOnly}
                <div class="mode-toggle">
                    <label>
                        <input
                            type="radio"
                            name="modeA"
                            value="default"
                            checked
                            bind:this={normalToggleA}
                            on:change={() => setRenderMode(viewerA, "default")}
                        />
                        <Cube class="mode-toggle-icon" />
                    </label>
                    <label>
                        <input
                            type="radio"
                            name="modeA"
                            value="wireframe"
                            bind:this={wireframeToggleA}
                            on:change={() => setRenderMode(viewerA, "wireframe")}
                        />
                        <WatsonHealth3DPrintMesh class="mode-toggle-icon" />
                    </label>
                </div>
            {/if}
        </div>
        <div bind:this={containerB} class="canvas-container">
            <div bind:this={overlayB} class="loading-overlay">
                <div class="loading-bar">
                    <div bind:this={loadingBarFillB} class="loading-bar-fill" />
                </div>
            </div>
            <div bind:this={voteOverlayB} class="vote-overlay">{data.model2}</div>
            <canvas bind:this={canvasB} class="viewer-canvas" id="canvas2"></canvas>
            <div class="stats">
                {#if viewerB}
                    <p>vertex count: {viewerB.vertexCount}</p>
                {/if}
            </div>
            {#if viewerB && !viewerB.topoOnly}
                <div class="mode-toggle">
                    <label>
                        <input
                            type="radio"
                            name="modeB"
                            value="default"
                            checked
                            bind:this={normalToggleB}
                            on:change={() => setRenderMode(viewerB, "default")}
                        />
                        <Cube class="mode-toggle-icon" />
                    </label>
                    <label>
                        <input
                            type="radio"
                            name="modeB"
                            value="wireframe"
                            bind:this={wireframeToggleB}
                            on:change={() => setRenderMode(viewerB, "wireframe")}
                        />
                        <WatsonHealth3DPrintMesh class="mode-toggle-icon" />
                    </label>
                </div>
            {/if}
        </div>
    </div>
    {#if !voteOverlay}
        <div class="vote-buttons-container">
            <button class="vote-button" on:click={() => vote("A")}>A is Better</button>
            <button class="vote-button" on:click={() => vote("B")}>B is Better</button>
        </div>
        <div class="skip-container">
            <button class="vote-button" on:click={() => skip()}>Skip</button>
        </div>
    {/if}
{/if}
