import { db } from '$lib/server/db';
import { character, devilFruit, arc, characterOverride } from '$lib/server/db/schema';
import { eq, sql } from 'drizzle-orm';
import { fail } from '@sveltejs/kit';
import type { PageServerLoad, Actions } from './$types';
import { writeFile } from 'fs/promises';
import { join } from 'path';
import { existsSync, mkdirSync } from 'fs';
import { env } from '$env/dynamic/private';

export const load: PageServerLoad = async () => {
	const [charactersData, devilFruits, arcs, overrides, statusesData, gendersData] = await Promise.all([
		db
			.select({
				id: character.id,
				name: character.name,
				gender: character.gender,
				age: character.age,
				affiliations: character.affiliations,
				devilFruitId: character.devilFruitId,
				hakiObservation: character.hakiObservation,
				hakiArmament: character.hakiArmament,
				hakiConqueror: character.hakiConqueror,
				bounty: character.bounty,
				height: character.height,
				origin: character.origin,
				firstAppearance: character.firstAppearance,
				pictureUrl: character.pictureUrl,
				epithets: character.epithets,
				status: character.status,
				url: character.url,
				arcId: character.arcId,
				isInDailyMode: character.isInDailyMode,
				arcName: arc.name,
				devilFruitName: devilFruit.name,
				devilFruitType: devilFruit.type
			})
			.from(character)
			.leftJoin(arc, eq(character.arcId, arc.id))
			.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
			.orderBy(character.name),
		db.select().from(devilFruit).orderBy(devilFruit.name),
		db.select().from(arc).orderBy(arc.name),
		db.select().from(characterOverride),
		db.selectDistinct({ status: character.status })
			.from(character)
			.where(sql`${character.status} IS NOT NULL AND ${character.status} != ''`),
		db.selectDistinct({ gender: character.gender })
			.from(character)
			.where(sql`${character.gender} IS NOT NULL AND ${character.gender} != ''`)
	]);

	// Create a map of overrides by characterId for easy lookup
	const overridesMap = new Map(overrides.map((o) => [o.characterId, o]));

	// Create maps for arcs and devil fruits to lookup names by ID
	const arcMap = new Map(arcs.map((a) => [a.id, a.name]));
	const devilFruitMap = new Map(devilFruits.map((f) => [f.id, { name: f.name, type: f.type }]));

	// Helper function to normalize data (parse JSON arrays)
	const normalizeArray = (value: any): any => {
		if (!value) return value;
		if (Array.isArray(value)) return value;
		if (typeof value === 'string' && value.includes('[')) {
			try {
				return JSON.parse(value);
			} catch {
				return value;
			}
		}
		return value;
	};

	// Merge character data with overrides
	const charactersWithOverrides = charactersData.map((char) => {
		const override = overridesMap.get(char.id);
		
		// Build displayValues by only applying non-null override fields
		const displayValues = { ...char } as any;
		if (override) {
			Object.keys(override).forEach((key) => {
				if (override[key as keyof typeof override] !== null && key !== 'characterId') {
					displayValues[key as keyof typeof displayValues] = override[key as keyof typeof override];
				}
			});
			
			// Update arcName if arcId was overridden
			if (override.arcId !== null && override.arcId !== undefined) {
				displayValues.arcName = arcMap.get(override.arcId) || null;
			}
			
			// Update devilFruitName and devilFruitType if devilFruitId was overridden
			if (override.devilFruitId !== null && override.devilFruitId !== undefined) {
				const fruit = devilFruitMap.get(override.devilFruitId);
				displayValues.devilFruitName = fruit?.name || null;
				displayValues.devilFruitType = fruit?.type || null;
			}
		}
		
		// Pre-normalize arrays (epithets, affiliations) for performance
		displayValues.epithets = normalizeArray(displayValues.epithets);
		displayValues.affiliations = normalizeArray(displayValues.affiliations);
		
		// Create search text for epithets
		displayValues.epithetsSearchText = Array.isArray(displayValues.epithets) 
			? displayValues.epithets.join(' ').toLowerCase()
			: (displayValues.epithets || '').toLowerCase();
		
		return {
			...char,
			override,
			displayValues
		};
	});

	return {
		characters: charactersWithOverrides,
		devilFruits,
		arcs,
		availableStatuses: statusesData
			.map(s => s.status)
			.filter((s): s is string => !!s)
			.sort((a, b) => a.localeCompare(b)),
		availableGenders: gendersData
			.map(g => g.gender)
			.filter((g): g is string => !!g)
			.sort((a, b) => a.localeCompare(b))
	};
};

export const actions: Actions = {
	update: async ({ request, locals }) => {
		if (!locals.user?.isAdmin) {
			return fail(401, { error: 'Unauthorized' });
		}

		const formData = await request.formData();
		const id = formData.get('id') as string;

		if (!id) {
			return fail(400, { error: 'Character ID is required' });
		}

		try {
			const [originalCharacter] = await db
				.select({
					hakiObservation: character.hakiObservation,
					hakiArmament: character.hakiArmament,
					hakiConqueror: character.hakiConqueror
				})
				.from(character)
				.where(eq(character.id, id))
				.limit(1);

			if (!originalCharacter) {
				return fail(404, { error: 'Character not found' });
			}

			const updates: Record<string, any> = {};

			// Handle file upload
			const pictureFile = formData.get('pictureFile') as File;
			const hasUploadedPicture = !!pictureFile && pictureFile.size > 0;
			if (hasUploadedPicture) {
				try {
					const uploadsDir = env.UPLOADS_DIR || join(process.cwd(),'uploads');
					if (!existsSync(uploadsDir)) {
						mkdirSync(uploadsDir, { recursive: true });
					}

					// Get file extension
					const extension = pictureFile.name.split('.').pop();
					const filename = `${id}.${extension}`;
					const filepath = join(uploadsDir, filename);

					// Convert file to buffer and save
					const buffer = Buffer.from(await pictureFile.arrayBuffer());
					await writeFile(filepath, buffer);

					// Update pictureUrl to point to the handler route
					updates.pictureUrl = `/uploads/${filename}`;
				} catch (error) {
					console.error('File upload error:', error);
					return fail(500, { error: 'Failed to upload file' });
				}
			}

			formData.forEach((value, key) => {
				if (key !== 'id' && key !== 'pictureFile') {
					if (hasUploadedPicture && key === 'pictureUrl') {
						return;
					}
					// Handle integers (age, bounty, height)
					if (key === 'age' || key === 'bounty' || key === 'height') {
						const strValue = value as string;
						updates[key] = strValue && strValue !== '' ? parseInt(strValue) : null;
					} 
					// Handle text IDs (devilFruitId, arcId)
					else if (key === 'devilFruitId' || key === 'arcId') {
						const strValue = value as string;
						updates[key] = strValue && strValue !== '' ? strValue : null;
					}
					// Handle checkboxes (haki fields) after parsing all form data
					else if (key === 'hakiObservation' || key === 'hakiArmament' || key === 'hakiConqueror') {
						return;
					}
					// Handle strings (name, gender, status, origin, affiliations, epithets, pictureUrl, url, firstAppearance)
					else {
						updates[key] = value || null;
					}
				}
			});

			const submittedHakiObservation = formData.has('hakiObservation');
			const submittedHakiArmament = formData.has('hakiArmament');
			const submittedHakiConqueror = formData.has('hakiConqueror');

			updates.hakiObservation =
				submittedHakiObservation === originalCharacter.hakiObservation ? null : submittedHakiObservation;
			updates.hakiArmament =
				submittedHakiArmament === originalCharacter.hakiArmament ? null : submittedHakiArmament;
			updates.hakiConqueror =
				submittedHakiConqueror === originalCharacter.hakiConqueror ? null : submittedHakiConqueror;

			// Update or insert into characterOverride table
			await db
				.insert(characterOverride)
				.values({ characterId: id, ...updates })
				.onConflictDoUpdate({ target: characterOverride.characterId, set: updates });

			return { success: true };
		} catch (error) {
			console.error('Character update error:', error);
			return fail(500, { error: 'Failed to update character' });
		}
	},

	delete: async ({ request, locals }) => {
		if (!locals.user?.isAdmin) {
			return fail(401, { error: 'Unauthorized' });
		}

		const formData = await request.formData();
		const id = formData.get('id') as string;

		if (!id) {
			return fail(400, { error: 'Character ID is required' });
		}

		try {
			await db.delete(character).where(eq(character.id, id));
			return { success: true };
		} catch (error) {
			console.error('Character delete error:', error);
			return fail(500, { error: 'Failed to delete character' });
		}
	},

	toggleDailyMode: async ({ request, locals }) => {
		if (!locals.user?.isAdmin) {
			return fail(401, { error: 'Unauthorized' });
		}

		const formData = await request.formData();
		const id = formData.get('id') as string;
		const isInDailyMode = formData.get('isInDailyMode') === 'true';

		if (!id) {
			return fail(400, { error: 'Character ID is required' });
		}

		try {
			await db.update(character)
				.set({ isInDailyMode })
				.where(eq(character.id, id));

			return { success: true };
		} catch (error) {
			console.error('Toggle daily mode error:', error);
			return fail(500, { error: 'Failed to toggle daily mode' });
		}
	}
};
