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

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

const characterWithRelationsSelect = {
	id: character.id,
	name: character.name,
	frName: character.frName,
	gender: character.gender,
	age: character.age,
	affiliation: character.affiliation,
	frAffiliation: character.frAffiliation,
	devilFruitId: character.devilFruitId,
	devilFruitName: devilFruit.name,
	devilFruitType: devilFruit.type,
	hakiObservation: character.hakiObservation,
	hakiArmament: character.hakiArmament,
	hakiConqueror: character.hakiConqueror,
	bounty: character.bounty,
	height: character.height,
	origin: character.origin,
	frOrigin: character.frOrigin,
	firstAppearance: character.firstAppearance,
	pictureUrl: character.pictureUrl,
	epithets: character.epithets,
	frEpithets: character.frEpithets,
	status: character.status,
	url: character.url,
	frUrl: character.frUrl,
	arcId: character.arcId,
	arcName: arc.name,
	frArcName: arc.frName,
};

export type CharacterWithRelations = Character & {
	devilFruitName: string | null;
	devilFruitType: string | null;
	arcName: string | null;
	frArcName: 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 pickDailyCharacter(characters: CharacterWithRelations[], date: Date): CharacterWithRelations {
	const timestamp = getDateKey(date);
	const daysSinceEpoch = Math.floor(timestamp / 1000 / 60 / 60 / 24);
	// Combine timestamp with random seed to avoid predictable results
	const combinedSeed = (daysSinceEpoch + Math.floor(RANDOM_SEED * 1000000)) % characters.length;
	return characters[combinedSeed];
}

export async function getDailyModeCharacters(): Promise<CharacterWithRelations[]> {
	return (await db
		.select(characterWithRelationsSelect)
		.from(character)
		.leftJoin(arc, eq(character.arcId, arc.id))
		.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
		.where(eq(character.isInDailyMode, true))
		.all()) as CharacterWithRelations[];
}

export async function getAllCharacters(): Promise<CharacterWithRelations[]> {
	return (await db
		.select(characterWithRelationsSelect)
		.from(character)
		.leftJoin(arc, eq(character.arcId, arc.id))
		.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
		.all()) as CharacterWithRelations[];
}

export async function getCharacterById(characterId: string): Promise<CharacterWithRelations | null> {
	const [found] = await db
		.select(characterWithRelationsSelect)
		.from(character)
		.leftJoin(arc, eq(character.arcId, arc.id))
		.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
		.where(eq(character.id, characterId))
		.limit(1);

	if (!found) {
		return null;
	}

	return found as CharacterWithRelations
}

export async function getOrCreateTodayCharacter(
	characters?: CharacterWithRelations[],
	date: Date = new Date()
): Promise<CharacterWithRelations | null> {
	const dailyCharacters = characters ?? (await getDailyModeCharacters());
	
	if (dailyCharacters.length === 0) {
		return null;
	}

	const today = normalizeDay(date);
	const todayDate = getDateKey(today);

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

	if (existingEntry?.characterId) {
		return (
			dailyCharacters.find((currentCharacter) => currentCharacter.id === existingEntry.characterId) ??
			(await getCharacterById(existingEntry.characterId))
		);
	}

	const recentHistory = await db
		.select({ characterId: characterHistory.characterId })
		.from(characterHistory)
		.orderBy(desc(characterHistory.date))
		.limit(100);

	const excludedIds = new Set(recentHistory.map((entry) => entry.characterId));
	const availableCharacters = dailyCharacters.filter((currentCharacter) => !excludedIds.has(currentCharacter.id));

	const dailyCharacter = pickDailyCharacter(
		availableCharacters.length > 0 ? availableCharacters : dailyCharacters,
		today
	);

	try {
		await db.insert(characterHistory).values({
			characterId: dailyCharacter.id,
			date: todayDate,
			createdAt: Date.now(),
			updatedAt: Date.now()
		});
	} catch (error) {
		console.error('Failed to record daily character:', error);
	}

	return dailyCharacter;
}

export async function getYesterdayCharacter(
	date: Date = new Date(),
	characters?: CharacterWithRelations[]
): Promise<CharacterWithRelations | null> {
	const yesterday = new Date(date);
	yesterday.setDate(yesterday.getDate() - 1);
	const yesterdayDate = getDateKey(yesterday);

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

	if (!yesterdayEntry?.characterId) {
		return null;
	}

	if (characters) {
		return (
			characters.find((currentCharacter) => currentCharacter.id === yesterdayEntry.characterId) ??
			(await getCharacterById(yesterdayEntry.characterId))
		);
	}

	return getCharacterById(yesterdayEntry.characterId);
}

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

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

	return result?.won ?? 0;
}