import { fail, redirect } from '@sveltejs/kit';
import type { Actions, PageServerLoad } from './$types';
import { auth } from '$lib/server/auth';
import { db } from '$lib/server/db';
import { session, userCharacterHistory, characterHistory, character, friendship, user } from '$lib/server/db/schema';
import { and, desc, eq, inArray, or, sql } from 'drizzle-orm';
import { APIError } from 'better-auth/api';

export const load: PageServerLoad = async (event) => {
	if (!event.locals.user) {
		return redirect(302, '/login');
	}

	const currentUserId = event.locals.user.id;

	// Fetch all sessions for this user
	const userSessions = await db
		.select()
		.from(session)
		.where(eq(session.userId, event.locals.user.id));

	// Fetch daily history for this user
	const dailyHistoryRaw = await db
		.select({
			id: userCharacterHistory.id,
			characterId: characterHistory.characterId,
			date: characterHistory.date,
			tryCount: userCharacterHistory.tryCount,
			triedCharacterIds: userCharacterHistory.triedCharacterIds,
			won: characterHistory.won,
			characterName: character.name,
			characterImage: character.pictureUrl
		})
		.from(userCharacterHistory)
		.innerJoin(characterHistory, eq(userCharacterHistory.characterHistoryId, characterHistory.id))
		.innerJoin(character, eq(characterHistory.characterId, character.id))
		.where(eq(userCharacterHistory.userId, event.locals.user.id))
		.orderBy(desc(characterHistory.date));

	const uniqueTriedCharacterIds = Array.from(new Set(
		dailyHistoryRaw.flatMap((entry) => entry.triedCharacterIds ?? [])
	));

	const triedCharacters = uniqueTriedCharacterIds.length > 0
		? await db
			.select({
				id: character.id,
				name: character.name,
				pictureUrl: character.pictureUrl
			})
			.from(character)
			.where(inArray(character.id, uniqueTriedCharacterIds))
		: [];

	const triedCharactersById = new Map(triedCharacters.map((entry) => [entry.id, entry]));

	const dailyHistory = dailyHistoryRaw.map((entry) => ({
		...entry,
		triedCharacters: (entry.triedCharacterIds ?? [])
			.map((characterId) => triedCharactersById.get(characterId))
			.filter((triedEntry): triedEntry is (typeof triedCharacters)[number] => !!triedEntry)
	}));

	const incomingRequests = await db
		.select({
			id: friendship.id,
			createdAt: friendship.createdAt,
			requesterId: friendship.requesterId,
			requesterName: user.name,
			requesterUsername: user.username,
			requesterImage: user.image
		})
		.from(friendship)
		.innerJoin(user, eq(friendship.requesterId, user.id))
		.where(and(eq(friendship.addresseeId, currentUserId), eq(friendship.status, 'pending')))
		.orderBy(desc(friendship.createdAt));

	const outgoingRequests = await db
		.select({
			id: friendship.id,
			createdAt: friendship.createdAt,
			addresseeId: friendship.addresseeId,
			addresseeName: user.name,
			addresseeUsername: user.username,
			addresseeImage: user.image
		})
		.from(friendship)
		.innerJoin(user, eq(friendship.addresseeId, user.id))
		.where(and(eq(friendship.requesterId, currentUserId), eq(friendship.status, 'pending')))
		.orderBy(desc(friendship.createdAt));

	const acceptedAsRequester = await db
		.select({
			id: friendship.id,
			createdAt: friendship.createdAt,
			friendId: friendship.addresseeId,
			friendName: user.name,
			friendUsername: user.username,
			friendImage: user.image
		})
		.from(friendship)
		.innerJoin(user, eq(friendship.addresseeId, user.id))
		.where(and(eq(friendship.requesterId, currentUserId), eq(friendship.status, 'accepted')));

	const acceptedAsAddressee = await db
		.select({
			id: friendship.id,
			createdAt: friendship.createdAt,
			friendId: friendship.requesterId,
			friendName: user.name,
			friendUsername: user.username,
			friendImage: user.image
		})
		.from(friendship)
		.innerJoin(user, eq(friendship.requesterId, user.id))
		.where(and(eq(friendship.addresseeId, currentUserId), eq(friendship.status, 'accepted')));

	const friends = [...acceptedAsRequester, ...acceptedAsAddressee].sort((a, b) => b.createdAt - a.createdAt);

	return {
		user: event.locals.user,
		sessions: userSessions,
		dailyHistory: dailyHistory,
		incomingRequests,
		outgoingRequests,
		friends
	};
};

export const actions: Actions = {
	updateProfile: async (event) => {
		if (!event.locals.user) {
			return redirect(302, '/login');
		}

		const formData = await event.request.formData();
		const name = formData.get('name')?.toString() ?? '';

		if (!name.trim()) {
			return fail(400, { message: 'Le nom ne peut pas être vide' });
		}

		try {
			await auth.api.updateUser({
				body: {
					name: name.trim()
				},
				headers: event.request.headers
			});
		} catch (error) {
			if (error instanceof APIError) {
				return fail(400, { message: error.message || 'Erreur lors de la mise à jour' });
			}
			return fail(500, { message: 'Erreur inattendue' });
		}

		return { success: true };
	},
	changePassword: async (event) => {
		if (!event.locals.user) {
			return redirect(302, '/login');
		}

		const formData = await event.request.formData();
		const oldPassword = formData.get('oldPassword')?.toString() ?? '';
		const newPassword = formData.get('newPassword')?.toString() ?? '';
		const confirmPassword = formData.get('confirmPassword')?.toString() ?? '';

		if (!oldPassword.trim()) {
			return fail(400, { message: 'Le mot de passe actuel est requis' });
		}

		if (!newPassword.trim()) {
			return fail(400, { message: 'Le nouveau mot de passe est requis' });
		}

		if (newPassword !== confirmPassword) {
			return fail(400, { message: 'Les mots de passe ne correspondent pas' });
		}

		if (newPassword.length < 8) {
			return fail(400, { message: 'Le mot de passe doit contenir au moins 8 caractères' });
		}

		try {
			await auth.api.changePassword({
				body: {
					currentPassword: oldPassword,
					newPassword
				},
				headers: event.request.headers
			});
		} catch (error) {
			if (error instanceof APIError) {
				return fail(400, { message: error.message || 'Erreur lors du changement de mot de passe' });
			}
			return fail(500, { message: 'Erreur inattendue' });
		}

		return { success: true };
	},
	revokeSession: async (event) => {
		if (!event.locals.user) {
			return redirect(302, '/login');
		}

		const formData = await event.request.formData();
		const sessionId = formData.get('sessionId')?.toString() ?? '';

		if (!sessionId) {
			return fail(400, { message: 'ID de session manquant' });
		}

		try {
			// Delete the session from database
			await db.delete(session).where(eq(session.id, sessionId));
		} catch (error) {
			console.error('Error revoking session:', error);
			return fail(500, { message: 'Erreur lors de la révocation de la session' });
		}

		return { success: true, message: 'Session révoquée avec succès' };
	},
	sendFriendRequest: async (event) => {
		if (!event.locals.user) {
			return redirect(302, '/login');
		}

		const formData = await event.request.formData();
		const friendUsername = formData.get('friendUsername')?.toString().trim() ?? '';

		if (!friendUsername) {
			return fail(400, { message: 'Nom d\'utilisateur requis pour envoyer une demande' });
		}

		const me = event.locals.user;
		const myUsername = (me as { username?: string }).username;

		if (myUsername && friendUsername.toLowerCase() === myUsername.toLowerCase()) {
			return fail(400, { message: 'Tu ne peux pas t\'ajouter toi-même' });
		}

		const [targetUser] = await db
			.select({ id: user.id, username: user.username })
			.from(user)
			.where(sql`lower(${user.username}) = ${friendUsername.toLowerCase()}`)
			.limit(1);

		if (!targetUser) {
			return fail(404, { message: 'Aucun utilisateur trouvé avec ce nom d\'utilisateur' });
		}

		const [existing] = await db
			.select()
			.from(friendship)
			.where(
				or(
					and(eq(friendship.requesterId, me.id), eq(friendship.addresseeId, targetUser.id)),
					and(eq(friendship.requesterId, targetUser.id), eq(friendship.addresseeId, me.id))
				)
			)
			.limit(1);

		const now = Date.now();

		if (!existing) {
			await db.insert(friendship).values({
				requesterId: me.id,
				addresseeId: targetUser.id,
				status: 'pending',
				createdAt: now,
				updatedAt: now
			});
			return { success: true, message: 'Demande d\'ami envoyée' };
		}

		if (existing.status === 'accepted') {
			return fail(400, { message: 'Vous êtes déjà amis' });
		}

		if (existing.status === 'pending') {
			if (existing.requesterId === targetUser.id && existing.addresseeId === me.id) {
				await db
					.update(friendship)
					.set({
						status: 'accepted',
						updatedAt: now
					})
					.where(eq(friendship.id, existing.id));
				return { success: true, message: 'Demande acceptée automatiquement' };
			}

			return fail(400, { message: 'Demande déjà envoyée' });
		}

		await db
			.update(friendship)
			.set({
				requesterId: me.id,
				addresseeId: targetUser.id,
				status: 'pending',
				updatedAt: now
			})
			.where(eq(friendship.id, existing.id));

		return { success: true, message: 'Demande d\'ami envoyée' };
	},
	acceptFriendRequest: async (event) => {
		if (!event.locals.user) {
			return redirect(302, '/login');
		}

		const formData = await event.request.formData();
		const friendshipId = formData.get('friendshipId')?.toString() ?? '';

		if (!friendshipId) {
			return fail(400, { message: 'Demande invalide' });
		}

		const now = Date.now();
		const result = await db
			.update(friendship)
			.set({ status: 'accepted', updatedAt: now })
			.where(
				and(
					eq(friendship.id, friendshipId),
					eq(friendship.addresseeId, event.locals.user.id),
					eq(friendship.status, 'pending')
				)
			)
			.returning({ id: friendship.id });

		if (result.length === 0) {
			return fail(404, { message: 'Demande introuvable' });
		}

		return { success: true, message: 'Demande acceptée' };
	},
	declineFriendRequest: async (event) => {
		if (!event.locals.user) {
			return redirect(302, '/login');
		}

		const formData = await event.request.formData();
		const friendshipId = formData.get('friendshipId')?.toString() ?? '';

		if (!friendshipId) {
			return fail(400, { message: 'Demande invalide' });
		}

		const now = Date.now();
		const result = await db
			.update(friendship)
			.set({ status: 'declined', updatedAt: now })
			.where(
				and(
					eq(friendship.id, friendshipId),
					eq(friendship.addresseeId, event.locals.user.id),
					eq(friendship.status, 'pending')
				)
			)
			.returning({ id: friendship.id });

		if (result.length === 0) {
			return fail(404, { message: 'Demande introuvable' });
		}

		return { success: true, message: 'Demande refusée' };
	},
	cancelFriendRequest: async (event) => {
		if (!event.locals.user) {
			return redirect(302, '/login');
		}

		const formData = await event.request.formData();
		const friendshipId = formData.get('friendshipId')?.toString() ?? '';

		if (!friendshipId) {
			return fail(400, { message: 'Demande invalide' });
		}

		const result = await db
			.delete(friendship)
			.where(
				and(
					eq(friendship.id, friendshipId),
					eq(friendship.requesterId, event.locals.user.id),
					eq(friendship.status, 'pending')
				)
			)
			.returning({ id: friendship.id });

		if (result.length === 0) {
			return fail(404, { message: 'Demande introuvable' });
		}

		return { success: true, message: 'Demande annulée' };
	},
	removeFriend: async (event) => {
		if (!event.locals.user) {
			return redirect(302, '/login');
		}

		const formData = await event.request.formData();
		const friendshipId = formData.get('friendshipId')?.toString() ?? '';

		if (!friendshipId) {
			return fail(400, { message: 'Relation invalide' });
		}

		const result = await db
			.delete(friendship)
			.where(
				and(
					eq(friendship.id, friendshipId),
					eq(friendship.status, 'accepted'),
					or(eq(friendship.requesterId, event.locals.user.id), eq(friendship.addresseeId, event.locals.user.id))
				)
			)
			.returning({ id: friendship.id });

		if (result.length === 0) {
			return fail(404, { message: 'Relation introuvable' });
		}

		return { success: true, message: 'Ami supprimé' };
	}
};
