import Elysia, { status } from "elysia";
import z from "zod";
import { and, count, eq, getTableColumns, not, notExists, or } from "drizzle-orm";
import { config, db, plugins } from "../app";
import * as schema from "@schema/app";
import { findPlatform } from "./services/utils";
import { FrontEndPlatformType } from "@simeonradivoev/gameflow-sdk/shared";

export default new Elysia()
    .get('/platforms', async () =>
    {
        const localPlatforms = await db.select({ ...getTableColumns(schema.platforms), game_count: count(schema.games.id) })
            .from(schema.platforms)
            .leftJoin(schema.games, eq(schema.games.platform_id, schema.platforms.id))
            .groupBy(schema.platforms.id);

        const localPlatformSet = new Set(localPlatforms.filter(p => p.game_count > 0).map(p => p.slug));

        const remotePlatforms: FrontEndPlatformType[] = [];

        await plugins.hooks.games.fetchPlatforms.promise({ platforms: remotePlatforms });

        await Promise.all(remotePlatforms.map(async p =>
        {
            p.hasLocal = localPlatformSet.has(p.slug);

            if (p.paths_screenshots.length <= 0)
            {
                const localScreenshots = await db.select({ id: schema.screenshots.id }).from(schema.games).leftJoin(schema.platforms, eq(schema.platforms.id, schema.games.platform_id)).where(eq(schema.platforms.slug, p.slug)).leftJoin(schema.screenshots, eq(schema.screenshots.game_id, schema.games.id)).limit(1);

                if (localScreenshots)
                    p.paths_screenshots.push(...localScreenshots.map(s => `/api/romm/screenshot/${s.id}`));
            }

            const localGames = await db.select({ id: schema.games.id, source: schema.games.source, souceId: schema.games.source_id }).from(schema.games).leftJoin(schema.platforms, eq(schema.platforms.id, schema.games.platform_id)).where(and(eq(schema.platforms.slug, p.slug), not(eq(schema.games.source, 'romm')))).groupBy(schema.games.id);
            p.game_count += localGames.length;
        }));

        const platformSlugSet = new Set(remotePlatforms.map(p => p.slug));

        const platforms: FrontEndPlatformType[] = [];
        platforms.push(...remotePlatforms);
        platforms.push(...await Promise.all(localPlatforms.filter(p => !platformSlugSet?.has(p.slug)).map(async p =>
        {
            const game = await db.query.games.findFirst({ where: eq(schema.games.platform_id, p.id) });
            let screenshots: { id: number; }[] = [];
            if (game)
            {
                screenshots = await db.query.screenshots.findMany({ where: eq(schema.screenshots.game_id, game.id), columns: { id: true } });
            }

            const platform: FrontEndPlatformType = {
                slug: p.slug,
                name: p.name,
                family_name: p.family_name,
                path_cover: `/api/romm/platform/local/${p.id}/cover`,
                game_count: p.game_count,
                updated_at: p.created_at,
                id: { source: 'local', id: String(p.id) },
                hasLocal: true,
                paths_screenshots: screenshots?.map(s => `/api/romm/screenshot/${s.id}`) ?? []

            };

            return platform;
        })));

        return { platforms };
    }).get('/platforms/:source/:id', async ({ params: { source, id } }) =>
    {
        if (source === 'local')
        {
            const localPlatform = await db.query.platforms.findFirst({ where: eq(schema.platforms.id, Number(id)) });
            if (localPlatform)
            {
                const platform: FrontEndPlatformType = {
                    slug: localPlatform.slug,
                    name: localPlatform.name,
                    family_name: localPlatform.family_name,
                    path_cover: `/api/romm/platform/local/${localPlatform.id}/cover`,
                    game_count: 0,
                    updated_at: localPlatform.created_at,
                    id: { source: 'local', id: String(localPlatform.id) },
                    hasLocal: true,
                    paths_screenshots: []
                };

                return platform;
            }

            return status("Not Found");
        } else
        {
            const remotePlatform = await plugins.hooks.games.fetchPlatform.promise({ source, id });
            if (!remotePlatform) return status("Not Found");
            const local = await db.query.platforms.findFirst({ where: or(eq(schema.platforms.slug, remotePlatform?.slug), eq(schema.platforms.name, remotePlatform?.name)) });
            return { ...remotePlatform, hasLocal: !!local };
        }
    }, { params: z.object({ source: z.string(), id: z.string() }) })
    .get('/platform/local/:id/cover', async ({ params: { id }, set }) =>
    {
        set.headers["cross-origin-resource-policy"] = 'cross-origin';

        const coverBlob = await db.query.platforms.findFirst({
            columns: {
                cover: true, cover_type: true

            }, where: eq(schema.platforms.id, id)
        });
        if (!coverBlob || !coverBlob.cover)
        {
            return status(404);
        }
        if (coverBlob.cover_type)
        {
            set.headers["content-type"] = coverBlob.cover_type;
        }
        return status(200, coverBlob.cover);
    }, { response: { 200: z.instanceof(Buffer<ArrayBufferLike>), 404: z.any() }, params: z.object({ id: z.coerce.number() }) })
    .post('/platform/:source/:id/update', async ({ params: { source, id } }) =>
    {
        const where: any[] = [];
        if (source === 'local')
        {
            where.push(eq(schema.platforms.id, Number(id)));
        } else
        {
            const remotePlatform = await plugins.hooks.games.fetchPlatform.promise({ source, id });
            if (remotePlatform)
            {
                where.push(eq(schema.platforms.slug, remotePlatform.slug));
            }
        }

        const localPlatform = await db.query.platforms.findFirst({
            where: or(...where)

        });
        if (!localPlatform) return status("Not Found");

        const platformLookup = await plugins.hooks.games.platformLookup.promise({
            slug: localPlatform.slug
        });
        let platformCover = await fetch(`${config.get('rommAddress') ?? 'https://demo.romm.app'}/assets/platforms/${localPlatform.slug}.svg`);
        if (!platformCover.ok && platformLookup?.url_logo)
        {
            platformCover = await fetch(platformLookup.url_logo);
        }

        await db.update(schema.platforms).set({
            name: platformLookup?.name,
            cover: Buffer.from(await platformCover.arrayBuffer()),
            cover_type: platformCover.headers.get('content-type'),
        }).where(eq(schema.platforms.id, localPlatform.id));
    })
    .delete('/platform/local/:id', async ({ params: { id } }) =>
    {
        const deleted = await db.delete(schema.platforms).where(and(eq(schema.platforms.id, Number(id)),
            notExists(
                db
                    .select()
                    .from(schema.games)
                    .where(eq(schema.games.platform_id, Number(id)))
            ))).returning();
        if (deleted.length <= 0) return status("Not Found");
    })
    .get('/platform/lookup/match/:source/:id', async ({ params: { source, id } }) =>
    {
        const platformLookup = await plugins.hooks.games.platformLookup.promise({ source, id });
        if (!platformLookup) return status("Not Found");
        const match = await findPlatform({
            system_slug: platformLookup.slug,
            platform: {
                source_slug: platformLookup.slug,
                source_id: Number(id),
                source: source,
                name: platformLookup.name
            }
        });
        return { details: platformLookup, match };
    }, {
        detail: {
            description: "Find matches of remote platform lookups. Returns the operations for each platform if it were to be imported. If platform locally exists. Will a new local platform be created from say romm. Unknown is returned if no match is found."
        }
    });