import { headers } from "next/headers";
import Link from "next/link";

import { MiiGender, MiiPlatform, Prisma } from "@prisma/client";
import { Icon } from "@iconify/react";

import crypto from "crypto";
import seedrandom from "seedrandom";

import { searchSchema } from "@/lib/schemas";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";

import SortSelect from "./sort-select";
import Carousel from "../../carousel";
import LikeButton from "../../like-button";
import DeleteMiiButton from "../delete-mii-button";
import Pagination from "./pagination";
import FilterMenu from "./filter-menu";

interface Props {
	searchParams: { [key: string]: string | string[] | undefined };
	userId?: number; // Profiles
	inLikesPage?: boolean; // Self-explanatory
}

export default async function MiiList({ searchParams, userId, inLikesPage }: Props) {
	const session = await auth();
	const parsed = searchSchema.safeParse(searchParams);
	if (!parsed.success) return <h1>{parsed.error.issues[0].message}</h1>;

	const { q: query, sort, tags, exclude, platform, gender, allowCopying, page = 1, limit = 24, seed } = parsed.data;

	// My Likes page
	let miiIdsLiked: number[] | undefined = undefined;

	if (inLikesPage && session?.user?.id) {
		const likedMiis = await prisma.like.findMany({
			where: { userId: Number(session.user.id) },
			select: { miiId: true },
		});
		miiIdsLiked = likedMiis.map((like) => like.miiId);
	}

	const where: Prisma.MiiWhereInput = {
		// Only show liked miis on likes page
		...(inLikesPage && miiIdsLiked && { id: { in: miiIdsLiked } }),
		// Searching
		...(query && {
			OR: [{ name: { contains: query, mode: "insensitive" } }, { tags: { has: query } }, { description: { contains: query, mode: "insensitive" } }],
		}),
		// Tag filtering
		...(tags && tags.length > 0 && { tags: { hasEvery: tags } }),
		...(exclude && exclude.length > 0 && { NOT: { tags: { hasSome: exclude } } }),
		// Platform
		...(platform && { platform: { equals: platform } }),
		// Gender
		...(gender && { gender: { equals: gender } }),
		// Allow Copying
		...(allowCopying && { allowedCopying: true }),
		// Profiles
		...(userId && { userId }),
	};

	const select: Prisma.MiiSelect = {
		id: true,
		// Don't show when userId is specified
		...(!userId && {
			user: {
				select: {
					id: true,
					name: true,
				},
			},
		}),
		platform: true,
		name: true,
		imageCount: true,
		tags: true,
		createdAt: true,
		gender: true,
		allowedCopying: true,
		// Mii liked check
		...(session?.user?.id && {
			likedBy: {
				where: { userId: Number(session.user.id) },
				select: { userId: true },
			},
		}),
		// Like count
		_count: {
			select: { likedBy: true },
		},
	};

	const skip = (page - 1) * limit;

	let totalCount: number;
	let filteredCount: number;
	let list: Prisma.MiiGetPayload<{ select: typeof select }>[];

	if (sort === "random") {
		// Get all IDs that match the where conditions
		const matchingIds = await prisma.mii.findMany({
			where,
			select: { id: true },
		});

		totalCount = matchingIds.length;
		filteredCount = Math.max(0, Math.min(limit, totalCount - skip));

		if (matchingIds.length === 0) return;

		// Use seed for consistent random results
		const randomSeed = seed || crypto.randomInt(0, 1_000_000_000);
		const rng = seedrandom(randomSeed.toString());

		// Randomize all IDs using the Durstenfeld algorithm
		for (let i = matchingIds.length - 1; i > 0; i--) {
			const j = Math.floor(rng() * (i + 1));
			[matchingIds[i], matchingIds[j]] = [matchingIds[j], matchingIds[i]];
		}

		// Convert to number[] array
		const selectedIds = matchingIds.slice(skip, skip + limit).map((i) => i.id);

		list = await prisma.mii.findMany({
			where: {
				id: { in: selectedIds },
			},
			select,
		});
	} else {
		// Sorting by likes, newest, or oldest
		let orderBy: Prisma.MiiOrderByWithRelationInput[];

		if (sort === "likes") {
			orderBy = [{ likedBy: { _count: "desc" } }, { name: "asc" }];
		} else if (sort === "oldest") {
			orderBy = [{ createdAt: "asc" }, { name: "asc" }];
		} else {
			// default to newest
			orderBy = [{ createdAt: "desc" }, { name: "asc" }];
		}

		[totalCount, filteredCount, list] = await Promise.all([
			prisma.mii.count({ where: { ...where, userId } }),
			prisma.mii.count({ where, skip, take: limit }),
			prisma.mii.findMany({
				where,
				orderBy,
				select,
				skip: (page - 1) * limit,
				take: limit,
			}),
		]);
	}

	const lastPage = Math.ceil(totalCount / limit);
	const miis = list.map(({ _count, likedBy, ...rest }) => ({
		...rest,
		likes: _count.likedBy,
		isLiked: session?.user?.id ? likedBy.length > 0 : false,
	}));

	return (
		<div className="w-full">
			<div className="bg-amber-50 border-2 border-amber-500 rounded-2xl shadow-lg p-4 flex justify-between items-center gap-2 mb-2 max-md:flex-col">
				<div className="flex items-center gap-2">
					{totalCount == filteredCount ? (
						<>
							<span className="text-2xl font-bold text-amber-900">{totalCount}</span>
							<span className="text-lg text-amber-700">{totalCount === 1 ? "Mii" : "Miis"}</span>
						</>
					) : (
						<>
							<span className="text-2xl font-bold text-amber-900">{filteredCount}</span>
							<span className="text-sm text-amber-700">of</span>
							<span className="text-lg font-semibold text-amber-800">{totalCount}</span>
							<span className="text-lg text-amber-700">Miis</span>
						</>
					)}
				</div>

				<div className="relative flex items-center justify-end gap-2 w-full md:max-w-2/3 max-md:justify-center">
					<FilterMenu />
					<SortSelect />
				</div>
			</div>

			<div className="grid grid-cols-4 gap-4 max-lg:grid-cols-3 max-md:grid-cols-2 max-[30rem]:grid-cols-1">
				{miis.map((mii) => (
					<div
						key={mii.id}
						className={`flex flex-col bg-zinc-50 rounded-3xl border-2 border-zinc-300 shadow-lg p-[0.8rem] transition hover:scale-105 hover:bg-cyan-100 hover:border-cyan-600 ${mii.platform === "SWITCH" ? "border-red-300" : "border-blue-200"}`}
					>
						<Carousel
							images={[
								`/mii/${mii.id}/image?type=mii`,
								...(platform === "THREE_DS" ? `/mii/${mii.id}/image?type=qr-code` : ""),
								...Array.from({ length: mii.imageCount }, (_, index) => `/mii/${mii.id}/image?type=image${index}`),
							]}
						/>

						<div className="p-4 flex flex-col gap-1 h-full">
							<Link href={`/mii/${mii.id}`} className="font-bold text-2xl line-clamp-1" title={mii.name}>
								{mii.name}
							</Link>
							<div id="tags" className="flex flex-wrap gap-1">
								{mii.tags.map((tag) => (
									<Link href={{ query: { tags: tag } }} key={tag} className="px-2 py-1 bg-orange-300 rounded-full text-xs">
										{tag}
									</Link>
								))}
							</div>

							<div className="mt-auto grid grid-cols-2 items-center">
								<LikeButton likes={mii.likes} miiId={mii.id} isLiked={mii.isLiked} isLoggedIn={session?.user != null} abbreviate />

								{!userId && (
									<Link href={`/profile/${mii.user?.id}`} className="text-sm text-right overflow-hidden text-ellipsis">
										@{mii.user?.name}
									</Link>
								)}

								{userId && Number(session?.user?.id) == userId && (
									<div className="flex gap-1 text-2xl justify-end text-zinc-400">
										<Link href={`/edit/${mii.id}`} title="Edit Mii" aria-label="Edit Mii" data-tooltip="Edit">
											<Icon icon="mdi:pencil" />
										</Link>
										<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii.likes} />
									</div>
								)}
							</div>
						</div>
					</div>
				))}
			</div>

			<Pagination lastPage={lastPage} />
		</div>
	);
}
