import { CheckIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { Button } from "@/components/ui/button";
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList,
  CommandSeparator,
} from "@/components/ui/command";
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeaderSrOnly,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
import { i18n } from "@/i18n";
import { DIALOG_NAMESPACE } from "@/i18n/resources";

type Selection = string | number;

// ============================================================
// Context
// ============================================================

type SelectDialogContextValue<K> = {
  multiple: boolean;
  open: boolean;
  setOpen: (open: boolean) => void;
  selectedKeys: Set<K>;
  toggle: (key: K) => void;
  isSelected: (key: K) => boolean;
};

// biome-ignore lint/suspicious/noExplicitAny: use any to allow any key type
const SelectDialogContext = createContext<SelectDialogContextValue<any> | null>(
  null,
);

function useSelectDialog<V extends Selection>() {
  const ctx = useContext<SelectDialogContextValue<V> | null>(
    SelectDialogContext,
  );
  if (!ctx) {
    throw new Error(
      "SelectDialog compound components must be used within <SelectDialog>",
    );
  }
  return ctx;
}

// ============================================================
// Group context (for items to register with their group)
// ============================================================

type GroupContextValue = {
  register: (value: Selection) => void;
  unregister: (value: Selection) => void;
};

const GroupContext = createContext<GroupContextValue | null>(null);

function useGroupContext() {
  return useContext(GroupContext);
}

// ============================================================
// Root
// ============================================================

export type SelectDialogProps<V extends Selection> = {
  title?: string;
  description?: string;
  children: React.ReactNode;
  open?: boolean;
  onOpenChange?: (open: boolean) => void;
} & (
  | {
      multiple?: false;
      value?: V;
      onValueChange?: (value: V) => void;
    }
  | {
      multiple: true;
      value?: V[];
      onValueChange?: (value: V[]) => void;
    }
);

export function SelectDialog<V extends Selection>({
  title,
  description,
  children,
  open: controlledOpen,
  onOpenChange,
  multiple = false,
  value,
  onValueChange,
}: SelectDialogProps<V>) {
  const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
  const isControlled = controlledOpen !== undefined;
  const open = isControlled ? controlledOpen : uncontrolledOpen;

  // Normalize value into a Set<string>
  const externalKeys = useMemo(() => {
    if (value === undefined) {
      return new Set<V>();
    }
    if (Array.isArray(value)) {
      return new Set(value);
    }
    return new Set([value]);
  }, [value]);

  // Internal draft state (for multi-select, commit on confirm)
  const [draftKeys, setDraftKeys] = useState<Set<V>>(externalKeys);

  // Sync draft when dialog opens
  const prevOpen = useRef(open);
  useEffect(() => {
    if (open && !prevOpen.current) {
      setDraftKeys(externalKeys);
    }
    prevOpen.current = open;
  }, [open, externalKeys]);

  const setOpen = useCallback(
    (next: boolean) => {
      if (!isControlled) {
        setUncontrolledOpen(next);
      }
      onOpenChange?.(next);
    },
    [isControlled, onOpenChange],
  );

  const toggle = useCallback(
    (key: V) => {
      if (!multiple) {
        // Single: commit immediately and close
        (onValueChange as ((v: V) => void) | undefined)?.(key);
        setOpen(false);
      } else {
        // Multi: update draft
        setDraftKeys((prev) => {
          const next = new Set(prev);
          if (next.has(key)) {
            next.delete(key);
          } else {
            next.add(key);
          }
          return next;
        });
      }
    },
    [multiple, onValueChange, setOpen],
  );

  const isSelected = useCallback(
    (key: V) => {
      if (!multiple) {
        return externalKeys.has(key);
      }
      return draftKeys.has(key);
    },
    [multiple, externalKeys, draftKeys],
  );

  // Expose commit / cancel for footer
  const ctx = useMemo<SelectDialogContextValue<V>>(
    () => ({
      multiple,
      open,
      setOpen,
      selectedKeys: !multiple ? externalKeys : draftKeys,
      toggle,
      isSelected,
    }),
    [multiple, open, setOpen, externalKeys, draftKeys, toggle, isSelected],
  );

  return (
    <SelectDialogContext.Provider value={ctx}>
      <Dialog open={open} onOpenChange={setOpen}>
        <DialogHeaderSrOnly title={title} description={description} />
        {children}
      </Dialog>
    </SelectDialogContext.Provider>
  );
}

// ============================================================
// Trigger
// ============================================================

export function SelectDialogTrigger({
  children,
}: {
  children: React.ReactNode;
}) {
  return <DialogTrigger asChild>{children}</DialogTrigger>;
}

// ============================================================
// Content (wraps DialogContent + Command)
// ============================================================

type SelectDialogContentProps = {
  children: React.ReactNode;
  className?: string;
};

export function SelectDialogContent({
  children,
  className,
}: SelectDialogContentProps) {
  return (
    <DialogContent
      showCloseButton={false}
      className={cn("gap-0 bg-popover p-0", className)}
    >
      <Command>{children}</Command>
    </DialogContent>
  );
}

// ============================================================
// Search
// ============================================================

export function SelectDialogSearch({
  placeholder = i18n.t("select.search_placeholder", { ns: DIALOG_NAMESPACE }),
}: {
  placeholder?: string;
}) {
  return <CommandInput placeholder={placeholder} />;
}

// ============================================================
// List
// ============================================================

type SelectDialogListProps = {
  children: React.ReactNode;
  className?: string;
};

export function SelectDialogList({
  children,
  className,
}: SelectDialogListProps) {
  return <CommandList className={className}>{children}</CommandList>;
}

// ============================================================
// Empty
// ============================================================

export function SelectDialogEmpty({
  children,
}: {
  children?: React.ReactNode;
}) {
  const { t } = useTranslation(DIALOG_NAMESPACE);
  return <CommandEmpty>{children ?? t("select.empty")}</CommandEmpty>;
}

// ============================================================
// Group heading with select all
// ============================================================

type SelectDialogGroupHeadingProps = {
  heading: string;
  allSelected: boolean;
  onSelectAll: () => void;
};

function SelectDialogGroupHeading({
  heading,
  allSelected,
  onSelectAll,
}: SelectDialogGroupHeadingProps) {
  const { multiple } = useSelectDialog();
  const { t } = useTranslation(DIALOG_NAMESPACE);

  if (!multiple) {
    return heading;
  }

  return (
    <div className="flex items-center justify-between">
      <span>{heading}</span>
      <Button
        variant="ghost"
        size="sm"
        className="h-auto px-1.5 py-0.5 text-xs"
        onClick={(e) => {
          e.stopPropagation();
          onSelectAll();
        }}
      >
        {allSelected
          ? t("select.deselect_all", { ns: DIALOG_NAMESPACE })
          : t("select.select_all", { ns: DIALOG_NAMESPACE })}
      </Button>
    </div>
  );
}

// ============================================================
// Group
// ============================================================

type SelectDialogGroupProps = {
  heading?: string;
  value?: string;
  children: React.ReactNode;
};

export function SelectDialogGroup({
  heading,
  value,
  children,
}: SelectDialogGroupProps) {
  const { toggle, isSelected } = useSelectDialog();
  const [itemValues, setItemValues] = useState<Set<Selection>>(new Set());

  const register = useCallback((itemValue: Selection) => {
    setItemValues((prev) => {
      const next = new Set(prev);
      next.add(itemValue);
      return next;
    });
  }, []);

  const unregister = useCallback((itemValue: Selection) => {
    setItemValues((prev) => {
      const next = new Set(prev);
      next.delete(itemValue);
      return next;
    });
  }, []);

  const groupCtx = useMemo(
    () => ({ register, unregister }),
    [register, unregister],
  );

  const allSelected = useMemo(() => {
    if (itemValues.size === 0) return false;
    for (const v of itemValues) {
      if (!isSelected(v)) return false;
    }
    return true;
  }, [itemValues, isSelected]);

  const handleSelectAll = useCallback(() => {
    for (const v of itemValues) {
      if (allSelected) {
        if (isSelected(v)) toggle(v);
      } else {
        if (!isSelected(v)) toggle(v);
      }
    }
  }, [itemValues, allSelected, isSelected, toggle]);

  return (
    <GroupContext.Provider value={groupCtx}>
      <CommandGroup
        heading={
          heading && (
            <SelectDialogGroupHeading
              heading={heading}
              allSelected={allSelected}
              onSelectAll={handleSelectAll}
            />
          )
        }
        value={value}
      >
        {children}
      </CommandGroup>
    </GroupContext.Provider>
  );
}

// ============================================================
// Item
// ============================================================

type SelectDialogItemProps<V extends Selection> = {
  value: V;
  keywords?: string[];
  children?: React.ReactNode;
  className?: string;
};

export function SelectDialogItem<V extends Selection>({
  value,
  keywords,
  children,
  className,
}: SelectDialogItemProps<V>) {
  const { toggle, isSelected } = useSelectDialog();
  const groupCtx = useGroupContext();
  const selected = isSelected(value);

  useEffect(() => {
    groupCtx?.register(value);
    return () => {
      groupCtx?.unregister(value);
    };
  }, [groupCtx, value]);

  return (
    <CommandItem
      value={value.toString()}
      keywords={keywords}
      onSelect={() => toggle(value)}
      className={className}
    >
      <CheckIcon
        className={cn("mr-2 size-4", selected ? "opacity-100" : "opacity-0")}
      />
      {children ?? value}
    </CommandItem>
  );
}

// ============================================================
// Separator
// ============================================================

export function SelectDialogSeparator() {
  return <CommandSeparator />;
}

// ============================================================
// Skeleton
// ============================================================

export function SelectDialogSkeleton() {
  return (
    <div className="space-y-1">
      <Skeleton className="h-8 w-full" />
      <Skeleton className="h-8 w-full" />
      <Skeleton className="h-8 w-full" />
    </div>
  );
}

// ============================================================
// Footer (multi-select confirm / cancel)
// ============================================================

type SelectDialogFooterProps<V extends Selection> = {
  confirmText?: string;
  cancelText?: string;
  onConfirm?: (keys: V[]) => void;
  onCancel?: () => void;
  children?: React.ReactNode;
};

export function SelectDialogFooter<V extends Selection>({
  confirmText,
  cancelText,
  onConfirm,
  onCancel,
  children,
}: SelectDialogFooterProps<V>) {
  const { t } = useTranslation(DIALOG_NAMESPACE);
  const { multiple, selectedKeys, setOpen } = useSelectDialog<V>();

  if (!multiple) {
    return null;
  }

  const resolvedConfirmText = confirmText ?? t("select.footer.confirm");
  const resolvedCancelText = cancelText ?? t("select.footer.cancel");

  const handleConfirm = () => {
    onConfirm?.(Array.from(selectedKeys));
    setOpen(false);
  };

  const handleCancel = () => {
    onCancel?.();
    setOpen(false);
  };

  return (
    <DialogFooter className="flex justify-between! px-2 py-2">
      <div className="space-x-2">{children}</div>
      <div className="space-x-2">
        <Button variant="outline" size="sm" onClick={handleCancel}>
          {resolvedCancelText}
        </Button>
        <Button size="sm" onClick={handleConfirm}>
          {resolvedConfirmText}
        </Button>
      </div>
    </DialogFooter>
  );
}

export function SelectDialogFooterAction({
  children,
  onClick,
  ...props
}: React.ComponentProps<typeof Button>) {
  const { setOpen } = useSelectDialog();
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    onClick?.(e);
    setOpen(false);
  };
  return (
    <Button variant="outline" size="sm" onClick={handleClick} {...props}>
      {children}
    </Button>
  );
}
