// Database Access Layer stuff goes here

import { v4 as uuidv4 } from 'uuid';
import { db, sql } from "../../database/db";
import { PicobookManifest } from '../types';

export type DbRelease = {
	id: string;
	slug: string;
	repo: string;
	version: string;
	carts: {name: string; rom: number[]}[];
	author: string;
	manifest: PicobookManifest;
}

export type DbReleaseInternal = {
	id: string;
	slug: string;
	repo: string;
	version: string;
	carts: {carts: {name: string; rom: number[]}[]};
	author: string;
	manifest: PicobookManifest;
}

const compareVersions = (a: string, b: string) => {
	const [a1, a2] = a.split(".").map(x => Number(x));
	const [b1, b2] = b.split(".").map(x => Number(x));
	if (a1 !== b1) {
		return a1 - b1;
	} else {
		return a2 - b2;
	}
}

const compareByVersion = (a: DbRelease, b: DbRelease) => compareVersions(a.version, b.version);

export const getReleases = async (where: {
	author: string;
	slug?: string;
	version?: string;
}): Promise<DbRelease[]> => {
	const {author, slug, version} = where;
	let rows: DbReleaseInternal[];
	if (!slug) {
		rows = await db.query(sql`
			SELECT * from releases
			WHERE
				author = ${author}
		`);
	} else if (!version) {
		rows = await db.query(sql`
			SELECT * from releases
			WHERE
				slug = ${slug} AND
				author = ${author}
		`);
	} else {
		rows = await db.query(sql`
			SELECT * from releases
			WHERE
				slug = ${slug} AND
				author = ${author} AND
				version = ${version}
		`);
	}
	return rows.map(row => ({...row, carts: row.carts.carts}));
}

export const getRelease = async (where: {
	author: string;
	slug: string;
	version?: string;
}) => {
	const {version} = where;
	const releases = await getReleases(where);
	if (version) {
		if (releases.length === 1) {
			return releases[0];
		} else {
			return null;
		}
	} else {
		if (releases.length < 1) {
			return null;
		}
		releases.sort(compareByVersion);
		return releases[releases.length-1];
	}
}

export const getAuthorGames = async (where: {
	author: string;
}) => {
	const releases = await getReleases(where);
	const games = releases.reduce((accum, curr) => {
		const found = accum.find(r => r.slug === curr.slug);
		if (found) {
			found.releases.push(curr);
		} else {
			accum.push({slug: curr.slug, releases: [curr]});
		}
		return accum;
	}, [] as {slug: string; releases: DbRelease[]}[]);
	games.forEach(game => {
		game.releases.sort(compareByVersion).reverse();
	});
	return games;
}

export const insertRelease = async (props: {manifest: PicobookManifest, carts: {name: string; rom: number[]}[]}) => {
	const {manifest, carts} = props;
	const {id: slug, author, repo, version} = manifest;
	const id = uuidv4();
	const now = new Date();
	await db.query(sql`
		INSERT INTO releases (id, slug, repo, version, author, carts, manifest, created_at)
		VALUES (${id}, ${slug}, ${repo}, ${version}, ${author}, ${{carts}}, ${manifest}, ${now})
	`);
	return id;
}
