import { desc, eq, inArray, like } from "drizzle-orm";
import { anime, animeSongLinks, artists, groups, songs } from "$lib/db/schema";
import type { ClientDb } from "./index";

/**
 * Client-side DB query helpers (read-only).
 *
 * These functions are intentionally side-effect free — seeding/overwriting the DB
 * file should be done separately (see `seed.ts`).
 *
 * We accept the concrete SQLocal-backed Drizzle DB type so callers get full type inference.
 */

export const DEFAULT_LIST_LIMIT = 20;
export const MAX_LIST_LIMIT = 200;

/**
 * Get a short list of anime entries for browsing.
 *
 * Sorted by (year desc, seasonId desc, annId desc).
 */
export async function getAnimeList(db: ClientDb, limit = DEFAULT_LIST_LIMIT) {
	const safeLimit = clampLimit(limit);

	// Explicit selection keeps payload small and gives a stable, inferred return type
	return db
		.select({
			annId: anime.annId,
			mainName: anime.mainName,
			year: anime.year,
			seasonId: anime.seasonId,
			malId: anime.malId,
		})
		.from(anime)
		.orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
		.limit(safeLimit);
}

/**
 * Simple `LIKE` search over `anime.main_name`.
 *
 * If you want fast/quality search later, consider shipping an FTS5 virtual table
 * in the snapshot DB, and querying that instead.
 */
export async function searchAnimeByName(
	db: ClientDb,
	query: string,
	limit = DEFAULT_LIST_LIMIT,
) {
	const q = query.trim();
	if (!q) return [];

	const safeLimit = clampLimit(limit);
	const pattern = `%${q}%`;

	return db
		.select({
			annId: anime.annId,
			mainName: anime.mainName,
			year: anime.year,
			seasonId: anime.seasonId,
			malId: anime.malId,
		})
		.from(anime)
		.where(like(anime.mainName, pattern))
		.orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
		.limit(safeLimit);
}

/**
 * Fetch a single anime plus its linked songs, including:
 * - anime name
 * - link metadata: type + number (OP1/ED2/etc)
 * - song metadata: song name, artist/group name, fileName for audio playback
 *
 * Note: this assumes the SQLite snapshot has `songs.file_name`, `songs.song_artist_id`,
 * and `songs.song_group_id` populated.
 */
export async function getAnimeWithSongsByAnnId(db: ClientDb, annId: number) {
	const animeRows = await db
		.select({
			annId: anime.annId,
			mainName: anime.mainName,
			year: anime.year,
			seasonId: anime.seasonId,
			malId: anime.malId,
		})
		.from(anime)
		.where(eq(anime.annId, annId))
		.limit(1);

	const foundAnime = animeRows[0];
	if (!foundAnime) return null;

	const rows = await db
		.select({
			annSongId: animeSongLinks.annSongId,
			type: animeSongLinks.type,
			number: animeSongLinks.number,

			songName: songs.name,
			fileName: songs.fileName,

			artistName: artists.name,
			groupName: groups.name,
		})
		.from(animeSongLinks)
		.innerJoin(songs, eq(songs.annSongId, animeSongLinks.annSongId))
		.leftJoin(artists, eq(artists.songArtistId, songs.songArtistId))
		.leftJoin(groups, eq(groups.songGroupId, songs.songGroupId))
		.where(eq(animeSongLinks.annId, annId))
		.orderBy(desc(animeSongLinks.type), desc(animeSongLinks.number));

	return {
		anime,
		songs: rows.map((r) => ({
			annSongId: r.annSongId,
			type: r.type,
			number: r.number,
			songName: r.songName,
			fileName: r.fileName,
			artistName: r.artistName ?? r.groupName ?? null,
		})),
	};
}

/**
 * Fetch songs for a list of MAL anime ids.
 *
 * This joins `anime` -> `anime_song_links` -> `songs` and includes artist/group name.
 *
 * Intended usage: MAL animelist entries give you `node.id` (MAL id); pass those ids here.
 */
export async function getSongsForMalAnimeIds(
	db: ClientDb,
	malAnimeIds: number[],
) {
	const ids = malAnimeIds.filter((n) => Number.isFinite(n));
	if (ids.length === 0) return [];

	return db
		.select({
			annId: anime.annId,
			malId: anime.malId,
			animeName: anime.mainName,
			year: anime.year,
			seasonId: anime.seasonId,

			annSongId: animeSongLinks.annSongId,
			type: animeSongLinks.type,
			number: animeSongLinks.number,

			songName: songs.name,
			fileName: songs.fileName,

			artistName: artists.name,
			groupName: groups.name,
		})
		.from(anime)
		.innerJoin(animeSongLinks, eq(animeSongLinks.annId, anime.annId))
		.innerJoin(songs, eq(songs.annSongId, animeSongLinks.annSongId))
		.leftJoin(artists, eq(artists.songArtistId, songs.songArtistId))
		.leftJoin(groups, eq(groups.songGroupId, songs.songGroupId))
		.where(inArray(anime.malId, ids))
		.orderBy(
			desc(anime.year),
			desc(anime.seasonId),
			desc(anime.annId),
			desc(animeSongLinks.type),
			desc(animeSongLinks.number),
		);
}

function clampLimit(limit: number) {
	const n = Number(limit);
	if (!Number.isFinite(n)) return DEFAULT_LIST_LIMIT;
	return Math.max(1, Math.min(MAX_LIST_LIMIT, Math.floor(n)));
}
