'use client';

import { ChevronDown } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import type { ReactNode } from 'react';

export interface TreeItem {
  id: number;
  label: string;
  description?: string;
  children: TreeItem[];
}

export function collectTreeItemIds<TItem extends TreeItem>(
  item: TItem,
): number[] {
  return [
    item.id,
    ...item.children.flatMap((child) => collectTreeItemIds(child as TItem)),
  ];
}

function TreeCheckbox({
  checked,
  indeterminate,
  onChange,
}: {
  checked: boolean;
  indeterminate: boolean;
  onChange: (checked: boolean) => void;
}) {
  const ref = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (ref.current) {
      ref.current.indeterminate = indeterminate;
    }
  }, [indeterminate]);

  return (
    <input
      ref={ref}
      type="checkbox"
      checked={checked}
      onChange={(event) => onChange(event.target.checked)}
      className="size-4 shrink-0 rounded-lg border-border accent-primary"
    />
  );
}

function CheckboxTreeNode<TItem extends TreeItem>({
  item,
  selectedIds,
  onToggle,
  renderMeta,
  showDescription = true,
}: {
  item: TItem;
  selectedIds: Set<number>;
  onToggle: (item: TItem, checked: boolean) => void;
  renderMeta?: (item: TItem) => ReactNode;
  showDescription?: boolean;
}) {
  const [isExpanded, setIsExpanded] = useState(true);
  const itemIds = collectTreeItemIds(item);
  const selectedCount = itemIds.filter((id) => selectedIds.has(id)).length;
  const checked = selectedCount === itemIds.length;
  const indeterminate = selectedCount > 0 && !checked;
  const hasChildren = item.children.length > 0;

  return (
    <li className="space-y-2">
      <div className="rounded-lg border border-border/60 bg-background px-3 py-2">
        <div className="flex items-center gap-2">
          {hasChildren ? (
            <button
              type="button"
              onClick={() => setIsExpanded((current) => !current)}
              aria-label={
                isExpanded ? `Collapse ${item.label}` : `Expand ${item.label}`
              }
              className="inline-flex size-5 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition hover:bg-muted hover:text-foreground"
            >
              <ChevronDown
                className={`size-4 transition-transform ${isExpanded ? '' : '-rotate-90'}`}
              />
            </button>
          ) : (
            <span className="size-5 shrink-0" aria-hidden="true" />
          )}
          <TreeCheckbox
            checked={checked}
            indeterminate={indeterminate}
            onChange={(isChecked) => onToggle(item, isChecked)}
          />
          <div className="flex min-w-0 flex-1 items-center gap-2">
            <span className="truncate text-sm font-medium leading-none">
              {item.label}
            </span>
            {renderMeta?.(item)}
          </div>
        </div>
        {showDescription && item.description ? (
          <p className="mt-1.5 pl-11 text-xs text-muted-foreground">
            {item.description}
          </p>
        ) : null}
      </div>
      {hasChildren && isExpanded ? (
        <ul className="ml-5 space-y-2 border-l border-border/70 pl-3">
          {item.children.map((child) => (
            <CheckboxTreeNode
              key={child.id}
              item={child as TItem}
              selectedIds={selectedIds}
              onToggle={onToggle}
              renderMeta={renderMeta}
              showDescription={showDescription}
            />
          ))}
        </ul>
      ) : null}
    </li>
  );
}

export function CheckboxTree<TItem extends TreeItem>({
  items,
  selectedIds,
  onSelectedIdsChange,
  renderMeta,
  emptyMessage = 'No items available.',
  showDescription = true,
}: {
  items: TItem[];
  selectedIds: number[];
  onSelectedIdsChange: (ids: number[]) => void;
  renderMeta?: (item: TItem) => ReactNode;
  emptyMessage?: string;
  showDescription?: boolean;
}) {
  const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds]);

  const handleToggle = (item: TItem, checked: boolean) => {
    const next = new Set(selectedSet);
    collectTreeItemIds(item).forEach((id) => {
      if (checked) {
        next.add(id);
      } else {
        next.delete(id);
      }
    });
    onSelectedIdsChange(Array.from(next));
  };

  if (items.length === 0) {
    return (
      <div className="rounded-lg border border-dashed p-6 text-center text-sm text-muted-foreground">
        {emptyMessage}
      </div>
    );
  }

  return (
    <ul className="space-y-2">
      {items.map((item) => (
        <CheckboxTreeNode
          key={item.id}
          item={item}
          selectedIds={selectedSet}
          onToggle={handleToggle}
          renderMeta={renderMeta}
          showDescription={showDescription}
        />
      ))}
    </ul>
  );
}
