import * as Icons from "@/components/Icons.tsx";
import type { AnyDocument, DocumentId } from "@/lib/types";
import {
  Dialog,
  DialogPanel,
  Transition,
  TransitionChild,
} from "@headlessui/react";
import { Fragment, useCallback, useState } from "react";

type Props<T extends AnyDocument> = {
  title?: React.ReactNode;
  error?: React.ReactNode;
  items: T[];
  renderRow: (item: T) => React.ReactNode;
  newItemForm: (onSubmit: () => void) => React.ReactNode;
  removeItem: (itemId: DocumentId) => void;
};

/**
 * DocumentList is a generic list component for displaying document items with a dialog for adding new items.
 *
 * @param title - The title displayed above the list (left-aligned)
 * @param items - The array of document items to display
 * @param renderRow - Function to render each row's content
 * @param newItemForm - Function that renders a form for creating a new item; receives an onSubmit callback
 */
export function DocumentList<T extends AnyDocument>({
  title,
  error,
  items,
  renderRow,
  newItemForm,
  removeItem,
}: Props<T>) {
  const [open, setOpen] = useState(false);
  const [isEditing, setIsEditing] = useState(false);

  const toggleEditMode = useCallback(
    () => setIsEditing((x) => !x),
    [setIsEditing],
  );

  // Handles closing the dialog after form submission
  const handleFormSubmit = (): void => {
    setOpen(false);
  };

  return (
    <section className="w-full">
      <div className="flex items-center justify-between">
        {title && <h2 className="text-xl font-bold text-slate-100">{title}</h2>}
        <div className="flex gap-2">
          {isEditing && (
            <button
              type="button"
              className="inline-flex items-center justify-center rounded-full bg-violet-600 hover:bg-violet-700 text-white w-8 h-8 focus:outline-none focus:ring-2 focus:ring-violet-400"
              aria-label="Add new item"
              onClick={() => setOpen(true)}
            >
              <Icons.Cross />
            </button>
          )}
          <button
            type="button"
            className="inline-flex items-center justify-center rounded-full bg-violet-600 hover:bg-violet-700 text-white w-8 h-8 focus:outline-none focus:ring-2 focus:ring-violet-400"
            aria-label={isEditing ? "Exit edit mode" : "Enter edit mode"}
            onClick={toggleEditMode}
          >
            {isEditing ? <Icons.Done /> : <Icons.Edit />}
          </button>
        </div>
      </div>
      {error && (
        <div className="bg-red-900 rounded p-4 text-slate-100">{error}</div>
      )}
      <ul className="flex flex-col space-y-2">
        {items.map((item) => (
          <li
            key={item.id}
            className="p-2 m-0 border-b-1 last:border-0 border-slate-700 flex flex-row justify-between items-center"
          >
            {renderRow(item)}

            {isEditing && (
              <div>
                <button
                  type="button"
                  className="inline-flex items-center justify-center rounded-full bg-violet-600 hover:bg-violet-700 text-white w-8 h-8 focus:outline-none focus:ring-2 focus:ring-violet-400"
                  aria-label="Remove item"
                  onClick={() => removeItem(item.id)}
                >
                  <Icons.Remove />
                </button>
              </div>
            )}
          </li>
        ))}
      </ul>
      <Transition show={open} as={Fragment}>
        <Dialog as="div" className="relative z-50" onClose={setOpen}>
          <TransitionChild
            as={Fragment}
            enter="ease-out duration-200"
            enterFrom="opacity-0"
            enterTo="opacity-100"
            leave="ease-in duration-150"
            leaveFrom="opacity-100"
            leaveTo="opacity-0"
          >
            <div className="fixed inset-0 bg-black bg-opacity-50 transition-opacity" />
          </TransitionChild>
          <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
            <TransitionChild
              as={Fragment}
              enter="ease-out duration-200"
              enterFrom="opacity-0 scale-95"
              enterTo="opacity-100 scale-100"
              leave="ease-in duration-150"
              leaveFrom="opacity-100 scale-100"
              leaveTo="opacity-0 scale-95"
            >
              <DialogPanel className="bg-slate-900 rounded-lg shadow-xl max-w-md w-full p-6 border border-slate-700 relative">
                {newItemForm(handleFormSubmit)}
                <button
                  type="button"
                  className="absolute top-3 right-3 text-slate-400 hover:text-red-400 focus:outline-none"
                  aria-label="Close dialog"
                  onClick={() => setOpen(false)}
                >
                  <span aria-hidden="true">×</span>
                </button>
              </DialogPanel>
            </TransitionChild>
          </div>
        </Dialog>
      </Transition>
    </section>
  );
}
