import { eq } from "drizzle-orm";
import { cache } from "./app";
import cacheSchema from "@schema/cache";
import { GithubReleaseSchema } from '@simeonradivoev/gameflow-sdk/shared';
import PQueue from "p-queue";
import z from "zod";

export const CACHE_KEYS = {
    ROM_PLATFORMS: 'rom-platforms',
    STORE_GAME: (path: string) => `store-game-${path}`,
    STORE_GAME_MANIFEST: 'store-game-manifest'
} as const;

// we aggressively cache github data so burst of calls is fine.
export const githubRequestQueue = new PQueue({ intervalCap: 60, interval: 1000 * 60 * 60, strict: true });

export async function getOrCached<T> (key: string, getter: (lastValue: T | undefined) => Promise<T>, options?: { expireMs?: number; force?: boolean; }): Promise<T>
{
    const cached = await cache.query.item_cache.findFirst({ where: eq(cacheSchema.item_cache.key, key) });
    const updated_at = new Date();

    if (cached && cached.expire_at > updated_at && !options?.force)
    {
        return cached.data as T;
    }

    const data = await getter(cached?.data as T);
    if (data === undefined) return data;

    const expire_at = options?.expireMs ? new Date(updated_at.getTime() + options.expireMs) : new Date(updated_at.getTime() + 24 * 60 * 60 * 1000);

    await cache.insert(cacheSchema.item_cache)
        .values({ key, data, updated_at, expire_at })
        .onConflictDoUpdate({
            target: cacheSchema.item_cache.key,
            set: { data, updated_at, expire_at }
        })
        .run();

    return data;
}

export async function getOrCachedGithubRelease (path: string, forceCheck?: boolean)
{
    return getOrCached<z.infer<typeof GithubReleaseSchema>>(`github-release-${path}`, () => githubRequestQueue.add(async () =>
    {
        const response = await fetch(`https://api.github.com/repos/${path}/releases/latest`, {
            method: "GET"
        });
        if (!response.ok) throw new Error(response.statusText);
        const release = await GithubReleaseSchema.parseAsync(await response.json());
        return release;
    }), { expireMs: 1000 * 60 * 60, force: forceCheck });
}