import { userCache } from "@/cache";
import { client } from "@/client";
import { Avatar, AvatarImage } from "@/components/ui/avatar";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { useBreakpoint } from "@/hooks/useBreakpoint";
import { useCache } from "@/hooks/useCache";
import { useLoading } from "@/hooks/useLoading";
import { DEBOUNCE } from "@/snippets";
import { UserId } from "common";
import { Role } from "common/the_api";
import { Array, Effect, HashSet, Match, Option, pipe } from "effect";
import { Loader2 } from "lucide-react";
import { useRef, useState } from "react";

export function Admin() {

	const [displayName, setDisplayName] = useState("");
	const [role, setRole] = useState<Role | "">("");

	const debounce = useRef(Effect.void);
	const breakpoint = useBreakpoint();
	const columns = breakpoint ? 4 : 1;

	const { isLoading, error, data: userIds } = useLoading(Effect.gen(function* () {
		yield* debounce.current;
		const data = yield* client.queryUsers({
			displayName: displayName !== "" ? Option.some(displayName) : Option.none(),
			role: role !== "" ? Option.some(role) : Option.none(),
			offset: 0,
			limit: 100,
		});
		return data;
	}), [displayName, role]);

	return (
		<div className="p-4 overflow-y-auto flex flex-col gap-4">
			<div className="flex flex-row flex-wrap items-baseline gap-4">
				<Input
					className="w-full md:w-[32ch]"
					type="text"
					placeholder="Nazwa wyświetlana"
					value={displayName}
					onChange={(e) => {
						setDisplayName(e.target.value);
						debounce.current = DEBOUNCE;
					}}
				/>
				<Select
					value={role}
					onValueChange={(role) => setRole(role === "None" ? "" : role as Role)}
				>
					<SelectTrigger className="w-full md:w-[24ch]">
						<SelectValue placeholder="Rola" />
					</SelectTrigger>
					<SelectContent>
						<SelectGroup>
							<SelectLabel>Role</SelectLabel>
							<SelectItem value="None"><em>Dowolna</em></SelectItem>
							<SelectItem value={Role.Viewer}>Przeglądanie</SelectItem>
							<SelectItem value={Role.Editor}>Edytor</SelectItem>
							<SelectItem value={Role.Admin}>Administrator</SelectItem>
						</SelectGroup>
					</SelectContent>
				</Select>
			</div>
			<Table>
				{breakpoint && (
					<TableHeader className="bg-white sticky top-0">
						<TableRow>
							<TableHead className="w-10" />
							<TableHead>Nazwa wyświetlana</TableHead>
							<TableHead>ID</TableHead>
							<TableHead>Role</TableHead>
						</TableRow>
					</TableHeader>
				)}
				<TableBody>
					{isLoading ? (
						<TableRow>
							<TableCell colSpan={columns} >
								<div className="flex items-center justify-center gap-2">
									<Loader2 className="animate-spin" />
									Ładowanie…
								</div>
							</TableCell>
						</TableRow>
					) : error !== null ? (
						<TableRow>
							<TableCell colSpan={columns} >
								<div className="flex items-center justify-center gap-2">
									Wystąpił błąd: {Match.value(error).pipe(
										Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
										Match.tag("Unauthenticated", () => "Zaloguj się, aby kontynuować"),
										Match.exhaustive,
									)}
								</div>
							</TableCell>
						</TableRow>
					) : (
						userIds.map((userId) => <UserRow key={userId} userId={userId} />)
					)}
				</TableBody>
			</Table>
		</div>
	);
}

namespace UserRow {
	export interface Props {
		readonly userId: UserId;
	}
}

function UserRow(props: UserRow.Props) {

	const { isLoading, error, data: user } = useCache(userCache, props.userId);

	const breakpoint = useBreakpoint();
	const columns = breakpoint ? 4 : 1;

	if (isLoading) {
		return (
			<TableRow>
				<TableCell colSpan={columns}>Ładowanie…</TableCell>
			</TableRow>
		);
	}

	if (error !== null) {
		return (
			<TableRow>
				<TableCell colSpan={columns}>
					Wystąpił błąd: {Match.value(error).pipe(
						Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
						Match.tag("NotFound", () => "Użytkownik nie istnieje"),
						Match.tag("Unauthenticated", () => "Zaloguj się, aby kontynuować"),
						Match.exhaustive,
					)}
				</TableCell>
			</TableRow>
		);
	}

	const rolesText = pipe(
		user.roles,
		HashSet.values,
		Array.join(", "),
	);

	return (
		<TableRow>
			{breakpoint ? (<>
				<TableCell>
					<Avatar className="shadow">
						<AvatarImage src={Option.getOrUndefined(user.avatarUrl)} />
					</Avatar>
				</TableCell>
				<TableCell>
					{Option.getOrUndefined(user.displayName)}
				</TableCell>
				<TableCell className="font-mono">
					{user.userId}
				</TableCell>
				<TableCell>
					{rolesText}
				</TableCell>
			</>) : (
				<TableCell className="flex flex-col">
					<div className="flex flex-row items-center gap-2 mb-2">
						<Avatar className="shadow">
							<AvatarImage src={Option.getOrUndefined(user.avatarUrl)} />
						</Avatar>
						<div>{Option.getOrUndefined(user.displayName)}</div>
					</div>
					<div className="text-xs font-mono">{user.userId}</div>
					<div className="text-xs">{rolesText}</div>
				</TableCell>
			)}
		</TableRow>
	);
}
