import { db } from '$lib/server/db';
import { character, devilFruit, devilFruitHistory, type DevilFruit, type Character } from '$lib/server/db/schema';
import { desc, eq, and } from 'drizzle-orm';

// Generate or get random seed for daily fruit selection
const RANDOM_SEED = Math.random();

const characterWithFruitSelect = {
	id: character.id,
	name: character.name,
	devilFruitId: character.devilFruitId,
	devilFruitName: devilFruit.name,
	devilFruitType: devilFruit.type,
	// include url if available from devilFruit join
	url: devilFruit.url
};

export type CharacterWithFruit = Character & {
	devilFruitName: string | null;
	devilFruitType: string | null;
};

export function getDateKey(date: Date): number {
	return normalizeDay(date).getTime();
}

export function normalizeDay(date: Date = new Date()): Date {
	const normalized = new Date(date);
	normalized.setHours(1, 0, 0, 0);
	return normalized;
}

function pickDailyFruit(fruits: DevilFruit[], date: Date): DevilFruit {
	const timestamp = getDateKey(date);
	const daysSinceEpoch = Math.floor(timestamp / 1000 / 60 / 60 / 24);
	const combinedSeed = (daysSinceEpoch + Math.floor(RANDOM_SEED * 1000000)) % fruits.length;
	return fruits[combinedSeed];
}

export async function getCharactersWithDevilFruit(): Promise<CharacterWithFruit[]> {
	const rows = (await db
		.select(characterWithFruitSelect)
		.from(character)
		.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
		.all()) as any[];

	return rows.filter((r) => r.devilFruitId) as CharacterWithFruit[];
}

export async function getAllDevilFruitsFromCharacters(): Promise<DevilFruit[]> {
	const characters = (await db
		.select(characterWithFruitSelect)
		.from(character)
		.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
		.all()) as any[];

	const map = new Map<string, DevilFruit>();
	for (const row of characters) {
		if (row.devilFruitId && row.devilFruitName) {
			if (!map.has(row.devilFruitId)) {
				map.set(row.devilFruitId, {
					id: row.devilFruitId,
					name: row.devilFruitName,
					type: row.devilFruitType ?? 'Unknown',
					url: row.url ?? null
				} as DevilFruit);
			}
		}
	}

	return Array.from(map.values());
}

export async function getFruitById(fruitId: string): Promise<DevilFruit | null> {
	const [found] = await db.select().from(devilFruit).where(eq(devilFruit.id, fruitId)).limit(1);
	return (found as DevilFruit) ?? null;
}

export async function getOrCreateTodayFruit(date: Date = new Date()): Promise<DevilFruit | null> {
	const today = normalizeDay(date);
	const todayDate = getDateKey(today);

	const [existingEntry] = await db
		.select()
		.from(devilFruitHistory)
		.where(eq(devilFruitHistory.date, todayDate))
		.limit(1);

	if (existingEntry?.devilFruitId) {
		return getFruitById(existingEntry.devilFruitId);
	}

	const allFruits = await getAllDevilFruitsFromCharacters();
	if (allFruits.length === 0) return null;

	const chosen = pickDailyFruit(allFruits, today);

	try {
		await db.insert(devilFruitHistory).values({
			devilFruitId: chosen.id,
			date: todayDate,
			createdAt: Date.now(),
			updatedAt: Date.now()
		});
	} catch (error) {
		console.error('Failed to record daily fruit:', error);
	}

	return chosen;
}

export async function getYesterdayFruit(date: Date = new Date()): Promise<DevilFruit | null> {
	const yesterday = new Date(date);
	yesterday.setDate(yesterday.getDate() - 1);
	const yesterdayDate = getDateKey(yesterday);

	const [yesterdayEntry] = await db
		.select()
		.from(devilFruitHistory)
		.where(eq(devilFruitHistory.date, yesterdayDate))
		.limit(1);

	if (!yesterdayEntry?.devilFruitId) return null;

	return getFruitById(yesterdayEntry.devilFruitId);
}

export async function getTodayFruitWinsCount(fruitId: string, date: Date = new Date()): Promise<number> {
	const today = normalizeDay(date);
	const todayDate = getDateKey(today);

	const [result] = await db
		.select({ won: devilFruitHistory.won })
		.from(devilFruitHistory)
		.where(and(eq(devilFruitHistory.devilFruitId, fruitId), eq(devilFruitHistory.date, todayDate)));

	return result?.won ?? 0;
}