<script setup lang="ts">
let { data: challengeData } = await useFetch('/api/pow/challenge');
let { data: powData } = await useFetch('/api/pow');

if (!challengeData || !powData) {
    throw createError({
        statusCode: 500,
        message: 'Failed to fetch data',
    });
}

const algorithms = {
    "argon2": {
        name: 'argon2',
        label: 'Argon2',
        strategies: ['leading_zeroes', 'target_number'],
    },
    "kctf": {
        name: 'kctf',
        label: 'kCTF',
        strategies: ['null'],
    },
    "sha256": {
        name: 'sha256',
        label: 'SHA256',
        strategies: ['leading_zeroes', 'target_number'],
    },
};

async function refresh() {
    challengeData.value = await $fetch('/api/pow/challenge');
    powData.value = await $fetch('/api/pow');

    resetCaptcha();
}

function resetCaptcha() {
    document.querySelector("pow-captcha")!.dispatchEvent(new CustomEvent('reset', {
        detail: {
            challenge: challengeData.value.challenge,
        }
    }));
}

let bench_results = ref([])
let start = ref(0);
let continue_bench: Promise<void> | null = null;

async function bench() {
    for (let algorithm_name in algorithms) {
        let algorithm = algorithms[algorithm_name as "sha256" | "argon2" | "kctf"];
        for (let strategy of algorithm.strategies) {
            for (let i = 1; i <= 3; i++) {
                // reduce statistical anomolies by running tests multiple times and averaging after tha fact
                for (let j = 0; j < 5; j++) {
                    let difficulty;
                    switch (strategy) {
                        case 'leading_zeroes':
                            difficulty = i;
                            break;
                        case 'null':
                        case 'target_number':
                            // these tests scale linearly, so to try to match the
                            // complexity of leading_zeroes, we grow the difficulty
                            // exponentially
                            difficulty = Math.pow(16, i);
                            break;
                    }
                    await changeAlgorithm(algorithm_name);
                    if (strategy !== 'null') {
                        await changeStrategy(strategy);
                    }
                    await $fetch('/api/pow/difficulty', {
                        method: 'PUT',
                        body: JSON.stringify({
                            difficulty: difficulty,
                        }),
                    })

                    // sleep for 300ms
                    await new Promise((resolve) => setTimeout(resolve, 750));

                    await refresh();

                    continue_bench = new Promise((resolve) => {
                        document.querySelector("pow-captcha")!.addEventListener('impost:solved', () => {
                            resolve();
                        });
                    });
                    start.value = performance.now();
                    document.querySelector("pow-captcha")!.dispatchEvent(new Event('solve'));
                    await continue_bench;
                    let end = performance.now();
                    const data = {
                        algorithm: algorithm_name,
                        cores: navigator.hardwareConcurrency,
                        strategy: strategy,
                        difficulty: difficulty,
                        time: end - start.value,
                    };

                    const should_scroll = document.documentElement.scrollTop + document.documentElement.clientHeight >= document.documentElement.scrollHeight;
                    bench_results.value.push(data);
                    if (should_scroll) {
                        document.documentElement.scrollTop = document.documentElement.scrollHeight;
                    }
                }
            }
        }
    }
}

function solved(ev: CustomEvent) {
    console.log("Solved:", ev.detail.solution);
}

async function changeAlgorithmEV(ev: Event) {
    changeAlgorithm(ev.target.value);

    refresh();
}

async function changeAlgorithm(algorithm: string) {
    await $fetch('/api/pow/algorithm', {
        method: 'PUT',
        body: JSON.stringify({
            algorithm: algorithm
        }),
    });
}

async function changeStrategyEV(ev: Event) {
    changeStrategy(ev.target.value);

    refresh();
}

async function changeStrategy(strategy: string) {
    await $fetch('/api/pow/strategy', {
        method: 'PUT',
        body: JSON.stringify({
            strategy: strategy
        }),
    });
}
</script>

<template>
    <pow-captcha challengeUrl="/api/pow" :challengejson="JSON.stringify(challengeData!.challenge)"
        @impost:solved="solved" />
    <div class="flex flex-row gap-4">
        <div class="flex flex-row gap-2" v-for="algorithm in algorithms" :key="algorithm.label">
            <input type="radio" name="algorithm" @change="changeAlgorithmEV" :value="algorithm.name"
                :id="algorithm.name" :checked="powData!.algorithm === algorithm.name"> <label :for="algorithm.name">{{
                    algorithm.label
                }}</label>
        </div>
    </div>
    <div v-if="algorithms[powData!.algorithm].strategies.length > 1 && powData!.algorithm === algorithms[powData!.algorithm].name"
        class="flex flex-row gap-4">
        <div class="flex flex-row gap-2" v-for="strategy in algorithms[powData!.algorithm].strategies">
            <input type="radio" name="strategy" @change="changeStrategyEV" :value="strategy" :id="strategy"
                :checked="powData!.strategy === strategy"> <label :for="strategy">{{ strategy }}</label>
        </div>
    </div>
    <input type="button" value="Start benchmark" @click="bench" />
    <div v-if="bench_results.length > 0">
        <table>
            <thead>
                <tr>
                    <th>Algorithm</th>
                    <th>Strategy</th>
                    <th>Difficulty</th>
                    <th>Time (ms)</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="result in bench_results" :key="result.algorithm + result.strategy + result.difficulty">
                    <td>{{ algorithms[result.algorithm].label }}</td>
                    <td>{{ result.strategy }}</td>
                    <td>{{ result.difficulty }}</td>
                    <td>{{ result.time }}</td>
                </tr>
            </tbody>
        </table>
    </div>
</template>