import { repertoireCache } from "@/cache";
import { client } from "@/client";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { useCache } from "@/hooks/useCache";
import { useLoading } from "@/hooks/useLoading";
import { created, DEBOUNCE, modified } from "@/snippets";
import { RepertoireId } from "common";
import { Cause, Effect, Match, Option, Scope } from "effect";
import { Loader2, Plus } from "lucide-react";
import { FormEventHandler, ReactNode, useId, useRef, useState } from "react";
import { Link, useNavigate } from "react-router-dom";

export function Repertoires() {

	const [name, setName] = useState("");

	const debounce = useRef(Effect.void);

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

	return (
		<div className="p-4 overflow-y-auto flex flex-col items-start gap-4">
			<div className="flex flex-row gap-4">
				<Dialog>
					<DialogTrigger asChild>
						<Button variant="outline">
							<Plus />Utwórz repertuar
						</Button>
					</DialogTrigger>
					<AddRepertoireDialogContent />
				</Dialog>
				<Input
					className="w-[32ch]"
					type="text"
					placeholder="Nazwa"
					value={name}
					onChange={(e) => {
						setName(e.target.value);
						debounce.current = DEBOUNCE;
					}}
				/>
			</div>
			<Table>
				<TableHeader className="bg-white sticky top-0">
					<TableRow>
						<TableHead>Nazwa</TableHead>
						<TableHead>Utwory</TableHead>
						<TableHead className="text-center">Dodano</TableHead>
						<TableHead className="text-center">Zmodyfikowano</TableHead>
					</TableRow>
				</TableHeader>
				<TableBody>
					{isLoading ? (
						<TableRow>
							<TableCell colSpan={4} >
								<div className="flex items-center justify-center gap-2">
									<Loader2 className="animate-spin" />
									Ładowanie…
								</div>
							</TableCell>
						</TableRow>
					) : error !== null ? (
						<TableRow>
							<TableCell colSpan={4} className="text-center">
								{Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${JSON.stringify(error)}`}
							</TableCell>
						</TableRow>
					) : (
						repertoireIds.map((repertoireId) => <RepertoireRow key={repertoireId} repertoireId={repertoireId} />)
					)}
				</TableBody>
			</Table>
		</div>
	);
}

namespace RepertoireRow {
	export interface Props {
		readonly repertoireId: RepertoireId;
	}
}

function RepertoireRow(props: RepertoireRow.Props) {

	const { isLoading, error, data: repertoire } = useCache(repertoireCache, props.repertoireId);

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

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

	const piecesParts: ReactNode[] = [];

	if (repertoire.entries.length === 0) {
		piecesParts.push(<em>Brak</em>);
	} else {
		const CUTOFF = 10;
		let text = repertoire.entries.slice(0, CUTOFF).map(({ name }) => name).join(", ");
		if (repertoire.entries.length > CUTOFF) {
			text += ", …";
		}

		piecesParts.push(text);
	}

	return (
		<TableRow>
			<TableCell>
				<Link className="underline" to={repertoire.repertoireId}>{repertoire.name}</Link>
			</TableCell>
			<TableCell>
				{...piecesParts}
			</TableCell>
			<TableCell className="text-center text-xs">
				{created(repertoire)}
			</TableCell>
			<TableCell className="text-center text-xs">
				{modified(repertoire)}
			</TableCell>
		</TableRow>
	);
}

function AddRepertoireDialogContent() {

	const navigate = useNavigate();

	const [name, setName] = useState("");

	const nameId = useId();

	const [isLoading, setIsLoading] = useState(false);

	const onSubmit: FormEventHandler<HTMLFormElement> = (e) => Effect.gen(function* () {
		e.preventDefault();

		yield* Effect.scopedWith((scope) => Effect.gen(function* () {

			yield* Scope.addFinalizer(scope, Effect.sync(() => setIsLoading(false)));

			setIsLoading(true);

			const { repertoireId } = yield* client.createRepertoire({
				name,
				entries: [],
			});

			navigate(repertoireId);
		}));
	}).pipe(Effect.runPromise);

	return (
		<DialogContent>
			<form onSubmit={onSubmit}>
				<DialogHeader>
					<DialogTitle>Utwórz repertuar</DialogTitle>
				</DialogHeader>
				<div className="grid items-baseline grid-cols-4 gap-4 py-4">
					<Label htmlFor={nameId} className="text-right">Nazwa</Label>
					<Input
						id={nameId}
						className="col-span-3"
						type="text"
						value={name}
						autoFocus
						required
						onChange={(e) => setName(e.target.value)}
					/>
				</div>
				<DialogFooter>
					<Button type="submit" disabled={isLoading}>
						{isLoading && <Loader2 className="animate-spin" />}
						Utwórz
					</Button>
				</DialogFooter>
			</form>
		</DialogContent>
	);
}
