<script lang="ts">
    import { onMount } from 'svelte';
    import { Client, simpleFetchHandler } from '@atcute/client';
    import type {} from '@atcute/atproto';
    import { fromStream } from '@atcute/repo';

    const { did, handle, pdsUrl, slowPokeMode } = $props();

    interface CountedCollection {
        collection: string;
        count: number;
    }

    //Shared State
    let loading = $state(true);
    let error: string | null = $state(null);
    //Download info stuff
    let downloadedBytes = $state(0);
    let downloadedMB = $derived((downloadedBytes / (1024 * 1024)).toFixed(2));
    //Ui counts for collections
    let collections = $state(new Array<CountedCollection>());
    let collectionsOrdered: Array<CountedCollection> = $derived([...collections].sort((a, b) => b.count - a.count));
    let totalRecords = $state(0);
    let currentCollection: string | null = $state(null);
    //Timer stuff
    let startTime = $state<number | null>(null);
    let endTime = $state<number | null>(null);
    let elapsedTime = $state('');
    let interval = $state<number | null>(null);
    //Just for the slow pokes
    let webCalls = $state(0);

    const calculateElapsedTime = () => {
        if (!startTime) return '0.00';
        const end = endTime ?? Date.now();
        elapsedTime = ((end - startTime) / 1000).toFixed(2);
    };

    const startTimer = () => {
        endTime = null;
        startTime = Date.now();
        interval = setInterval(() => {
            calculateElapsedTime();
        }, 250);
    };

    const stopTimer = () => {
        if (interval) {
            clearInterval(interval);
        }
        endTime = Date.now();
        calculateElapsedTime();
    };

    // Calls the getRepo endpoint to get a .car export to walk the repo. allows you to stream and access records as they are downloaded
    const getRepoStatsViaExport = async () => {
        const rpc = new Client({ handler: simpleFetchHandler({ service: pdsUrl }) });
        startTimer();
        try {
            const result = await rpc.get('com.atproto.sync.getRepo', {
                params: { did: did },
                as: 'stream',
            });

            if (!result.ok) {
                throw new Error(`HTTP error! status: ${result.status}`);
            }
            const repo = fromStream(result.data);

            try {
                //This reads the repo as it is downloaded. which was very cool and I didn't know it would do that
                for await (const entry of repo) {
                    // record here is the content of the atproto record
                    // console.log(entry.record);
                    let checkForCollection = collections.find(c => c.collection === entry.collection);
                    if (!checkForCollection) {
                        collections.push({ collection: entry.collection, count: 1 });
                    } else {
                        checkForCollection.count++;
                    }
                    downloadedBytes = entry.carEntry.entryEnd;
                    totalRecords++;
                }
            } finally {
                stopTimer();
            }
            loading = false;
        } catch (err) {
            stopTimer();
            console.log(err);
            console.error('Error fetching repo stats:', err);
            if (err instanceof Error) {
                error = err.message;
            } else {
                error = 'Unknown error: can check the console for more details';
            }
            loading = false;
        }
    };

    //An ungodly amount of api calls showing the speed difference between getting an export and walking the repo
    //via api calls
    const getRepoStatsTheLongWay = async () => {
        try {
            const rpc = new Client({ handler: simpleFetchHandler({ service: pdsUrl }) });
            startTimer();
            //We can make a call to get a list of collections in the repo
            let describeRepo = await rpc.get('com.atproto.repo.describeRepo', {
                params: {
                    repo: did
                }
            });
            if(!describeRepo.ok){
                throw new Error(`HTTP error! status: ${describeRepo.status}`);
            }
            webCalls++;
            //We go through each collection and get a list of records in it
            for (const collection of describeRepo.data.collections) {
                let totalRecordsInCollection = 0;
                currentCollection = collection;
                //Do the first call to get a cursor
                const firstCollectionList = await rpc.get('com.atproto.repo.listRecords', {
                    params: {
                        collection,
                        repo: did,
                        limit: 100,
                    }
                });
                webCalls++;
                if(!firstCollectionList.ok){
                    console.error(`HTTP error! status: ${firstCollectionList.status}`);
                    continue;
                }
                totalRecords += firstCollectionList.data.records.length;
                totalRecordsInCollection += firstCollectionList.data.records.length;

                let cursor = firstCollectionList.data.cursor;
                //Walk the collection till the cursor is undefined meaning no more records
                do {
                    const nextCollectionList = await rpc.get('com.atproto.repo.listRecords', {
                        params: {
                            collection,
                            repo: did,
                            limit: 100,
                            cursor
                        }
                    });
                    webCalls++;
                    if(!nextCollectionList.ok){
                        console.error(`HTTP error! status: ${nextCollectionList.status}`);
                        continue;
                    }
                    totalRecordsInCollection += nextCollectionList.data.records.length;
                    cursor = nextCollectionList.data.cursor;
                    totalRecords += nextCollectionList.data.records.length;

                } while (cursor !== undefined);
                collections.push({ collection: collection, count:totalRecordsInCollection });
            }
            loading = false;
            stopTimer();
        }
        catch (err) {
            loading = false;
            stopTimer();
            console.error('Error fetching repo stats:', err);
            if (err instanceof Error) {
                error = err.message;
            } else {
                error = 'Unknown error: can check the console for more details';
            }
        }
    };

    onMount(() => {
        if (slowPokeMode) {
            getRepoStatsTheLongWay();
        } else {
            getRepoStatsViaExport();
        }

    });

</script>

<div class="flex flex-col items-center gap-4">
    <div class="w-full flex justify-center">
        {#if slowPokeMode}
            <img
                alt="A Shellder biting a Slowpoke's tail, as seen in the Pokémon anime"
                src="/slowPoke.png"
                class="max-w-sm rounded-lg shadow-lg"
            >
        {:else}
            <img
                alt="text in a speech bubble that says 'Dude, wheres my car'"
                src="/dude.png"
                class="max-w-sm rounded-lg shadow-lg"
            >
        {/if}
    </div>

    {#if error}
        <div class="alert alert-error w-full">
            <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
            </svg>
            <span>{error}</span>
        </div>
    {/if}

    {#if loading && !slowPokeMode}
        <div class="flex items-center gap-3">
            <span class="loading loading-spinner loading-lg text-primary"></span>
            <div class="text-lg">
                <div class="font-semibold">Loading...</div>
                <div class="text-sm opacity-70">
                    <span class="badge badge-info">{downloadedMB} MB</span> downloaded in
                    <span class="badge badge-ghost">{elapsedTime}s</span>
                </div>
            </div>
        </div>
    {:else if loading && slowPokeMode}
        <div class="flex items-center gap-3">
            <span class="loading loading-spinner loading-lg text-primary"></span>
            <div class="text-lg">
                <div class="font-semibold">Loading...</div>
                <div class="text-sm opacity-70">
                    <span class="badge badge-info">{webCalls.toLocaleString()} web calls</span> made in
                    <span class="badge badge-ghost">{elapsedTime}s</span>
                </div>
            </div>
        </div>
    {:else}
        <div class="stats shadow bg-base-300">
            <div class="stat">
                <div class="stat-title">{slowPokeMode ? 'Web Calls Made' : 'Repo Size'}</div>
                <div class="stat-value text-primary">
                    {#if !slowPokeMode}
                        {downloadedMB} MB
                    {:else}
                        {webCalls.toLocaleString()}
                    {/if}
                </div>
                <div class="stat-desc">Fetched in {elapsedTime}s</div>
            </div>
        </div>
    {/if}

    {#if loading && currentCollection !== null}
        <div class="alert alert-info">
            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
            </svg>
            <span>Currently walking collection: <strong>{currentCollection}</strong></span>
        </div>
    {/if}

    {#if collectionsOrdered.length > 0}
        <div class="stats stats-vertical lg:stats-horizontal shadow bg-base-300 w-full">
            <div class="stat">
                <div class="stat-title">Total Records</div>
                <div class="stat-value text-secondary">{totalRecords.toLocaleString()}</div>
            </div>
            <div class="stat">
                <div class="stat-title">Different Collections</div>
                <div class="stat-value text-accent">{collectionsOrdered.length}</div>
            </div>
        </div>

        <div class="card bg-base-300 shadow-xl w-full">
            <div class="card-body">
                <h3 class="card-title">{handle}'s Collections Breakdown</h3>
                <ol class="list-decimal list-inside space-y-2">
                    {#each collectionsOrdered as collection (collection.collection)}
                        <li class="text-sm">
                            <a class="link font-mono text-primary" href="https://pdsls.dev/at://{did}/{collection.collection}" target="_blank">{collection.collection}</a>
                            <span class="badge badge-sm ml-2">{collection.count.toLocaleString()} records</span>
                        </li>
                    {/each}
                </ol>
            </div>
        </div>
    {/if}
</div>
