import { useMemo } from "react";
import { LuArrowUp, LuArrowDown, LuX, LuTrash2 } from "react-icons/lu";

import { Button } from "@components/Button";
import { Combobox, ComboboxOption } from "@components/Combobox";
import Card from "@components/Card";
import FieldLabel from "@components/FieldLabel";
import { SelectMenuBasic } from "@components/SelectMenuBasic";
import { MAX_KEYS_PER_STEP, DEFAULT_DELAY } from "@/constants/macros";
import { KeyboardLayout } from "@/keyboardLayouts";
import { keys, modifiers } from "@/keyboardMappings";
import { m } from "@localizations/messages.js";

// Filter out modifier keys since they're handled in the modifiers section
const modifierKeyPrefixes = ["Alt", "Control", "Shift", "Meta"];

const modifierOptions = Object.keys(modifiers).map(modifier => ({
  value: modifier,
  label: modifier.replace(/^(Control|Alt|Shift|Meta)(Left|Right)$/, "$1 $2"),
}));

const groupedModifiers: Record<string, typeof modifierOptions> = {
  Control: modifierOptions.filter(mod => mod.value.startsWith("Control")),
  Shift: modifierOptions.filter(mod => mod.value.startsWith("Shift")),
  Alt: modifierOptions.filter(mod => mod.value.startsWith("Alt")),
  Meta: modifierOptions.filter(mod => mod.value.startsWith("Meta")),
};

// not going to localize these since they're short time intervals
const basePresetDelays = [
  { value: "50", label: "50ms" },
  { value: "100", label: "100ms" },
  { value: "200", label: "200ms" },
  { value: "300", label: "300ms" },
  { value: "500", label: "500ms" },
  { value: "750", label: "750ms" },
  { value: "1000", label: "1000ms" },
  { value: "1500", label: "1500ms" },
  { value: "2000", label: "2000ms" },
];

const PRESET_DELAYS = basePresetDelays.map(delay => {
  if (Number.parseInt(delay.value, 10) === DEFAULT_DELAY) {
    return { ...delay, label: "Default" };
  }
  return delay;
});

interface MacroStep {
  keys: string[];
  modifiers: string[];
  delay: number;
}

interface MacroStepCardProps {
  step: MacroStep;
  stepIndex: number;
  onDelete?: () => void;
  onMoveUp?: () => void;
  onMoveDown?: () => void;
  onKeySelect: (option: { value: string | null; keys?: string[] }) => void;
  onKeyQueryChange: (query: string) => void;
  keyQuery: string;
  onModifierChange: (modifiers: string[]) => void;
  onDelayChange: (delay: number) => void;
  isLastStep: boolean;
  keyboard: KeyboardLayout;
}

const ensureArray = <T,>(arr: T[] | null | undefined): T[] => {
  return Array.isArray(arr) ? arr : [];
};

const keyDisplay = (keyDisplayMap: Record<string, string>, key: string): string => {
   return keyDisplayMap[key] || key
};

export function MacroStepCard({
  step,
  stepIndex,
  onDelete,
  onMoveUp,
  onMoveDown,
  onKeySelect,
  onKeyQueryChange,
  keyQuery,
  onModifierChange,
  onDelayChange,
  isLastStep,
  keyboard,
}: Readonly<MacroStepCardProps>) {
  const { keyDisplayMap } = keyboard;

  const keyOptions = useMemo(
    () =>
      Object.keys(keys)
        .filter(key => !modifierKeyPrefixes.some(prefix => key.startsWith(prefix)))
        .map(key => ({
          value: key,
          label: keyDisplay(keyDisplayMap, key),
        })),
    [keyDisplayMap],
  );

  const handleModifierToggle = (optionValue: string) => {
    const modifiersArray = ensureArray(step.modifiers);
    const isSelected = modifiersArray.includes(optionValue);
    const newModifiers = isSelected
      ? modifiersArray.filter(m => m !== optionValue)
      : [...modifiersArray, optionValue];
    onModifierChange(newModifiers);
  };

  const filteredKeys = useMemo(() => {
    const selectedKeys = ensureArray(step.keys);
    const availableKeys = keyOptions.filter(
      option => !selectedKeys.includes(option.value),
    );

    if (keyQuery === "") {
      return availableKeys;
    } else {
      return availableKeys.filter(option =>
        option.label.toLowerCase().includes(keyQuery.toLowerCase()),
      );
    }
  }, [keyOptions, keyQuery, step.keys]);

  return (
    <Card className="p-4">
      <div className="mb-2 flex items-center justify-between">
        <div className="flex items-center gap-1.5">
          <span className="flex h-6 w-5 items-center justify-center rounded-full bg-blue-100 text-xs font-semibold text-blue-700 dark:bg-blue-900/40 dark:text-blue-200">
            {stepIndex + 1}
          </span>
        </div>

        <div className="flex items-center space-x-2">
          <div className="flex items-center gap-1">
            <Button
              size="XS"
              theme="light"
              onClick={onMoveUp}
              disabled={stepIndex === 0}
              LeadingIcon={LuArrowUp}
            />
            <Button
              size="XS"
              theme="light"
              onClick={onMoveDown}
              disabled={isLastStep}
              LeadingIcon={LuArrowDown}
            />
          </div>
          {onDelete && (
            <Button
              size="XS"
              theme="light"
              className="text-red-500 dark:text-red-400"
              text={m.delete()}
              LeadingIcon={LuTrash2}
              onClick={onDelete}
            />
          )}
        </div>
      </div>

      <div className="mt-2 space-y-4">
        <div className="flex w-full flex-col gap-2">
          <FieldLabel
            label={m.macro_step_modifiers_label()}
            description={m.macro_step_modifiers_description()}
          />
          <div className="inline-flex flex-wrap gap-3">
            {Object.entries(groupedModifiers).map(([group, mods]) => (
              <div
                key={group}
                className="relative min-w-[120px] rounded-md border border-slate-200 p-2 dark:border-slate-700"
              >
                <span className="absolute -top-2.5 left-2 bg-white px-1 text-xs font-medium text-slate-500 dark:bg-slate-800 dark:text-slate-400">
                  {group}
                </span>
                <div className="flex flex-wrap gap-4 pt-1">
                  {mods.map(option => (
                    <Button
                      key={option.value}
                      size="XS"
                      theme={
                        ensureArray(step.modifiers).includes(option.value)
                          ? "primary"
                          : "light"
                      }
                      text={option.label.split(" ")[1] || option.label}
                      onClick={() => handleModifierToggle(option.value)}
                    />
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>

        <div className="flex w-full flex-col gap-1">
          <div className="flex items-center gap-1">
            <FieldLabel
              label={m.macro_step_keys_label()}
              description={m.macro_step_keys_description({ max: MAX_KEYS_PER_STEP })}
            />
          </div>

          {step.keys?.length > 0 && (
            <div className="flex flex-wrap gap-1 pb-2">
              {step.keys.map((key, keyIndex) => (
                <span
                  key={`key-${keyIndex}`}
                  className="inline-flex items-center rounded-md bg-blue-100 px-1 py-0.5 text-xs font-medium text-blue-800 dark:bg-blue-900/40 dark:text-blue-200"
                >
                  <span className="px-1">{keyDisplay(keyDisplayMap, key)}</span>
                  <Button
                    size="XS"
                    className=""
                    theme="blank"
                    onClick={() => {
                      const newKeys = step.keys.filter(
                        (_, i) => i !== keyIndex,
                      );
                      onKeySelect({ value: null, keys: newKeys });
                    }}
                    LeadingIcon={LuX}
                  />
                </span>
              ))}
            </div>
          )}
          <div className="relative w-full">
            <Combobox
              onChange={option => {
                const selectedOption = option as ComboboxOption | null;
                onKeySelect({ value: selectedOption?.value ?? null });
                onKeyQueryChange("");
              }}
              displayValue={() => keyQuery}
              onInputChange={onKeyQueryChange}
              options={() => filteredKeys}
              disabledMessage={m.macro_step_max_keys_reached({ max: MAX_KEYS_PER_STEP })}
              size="SM"
              immediate
              disabled={ensureArray(step.keys).length >= MAX_KEYS_PER_STEP}
              placeholder={
                ensureArray(step.keys).length >= MAX_KEYS_PER_STEP
                  ? m.macro_step_max_keys_reached()
                  : m.macro_step_search_for_key()
              }
              emptyMessage={m.macro_step_no_matching_keys_found()}
            />
          </div>
        </div>

        <div className="flex w-full flex-col gap-1">
          <div className="flex items-center gap-1">
            <FieldLabel
              label={m.macro_step_duration_label()}
              description={m.macro_step_duration_description()}
            />
          </div>
          <div className="flex items-center gap-3">
            <SelectMenuBasic
              size="SM"
              fullWidth
              value={step.delay.toString()}
              onChange={e => onDelayChange(Number.parseInt(e.target.value, 10))}
              options={PRESET_DELAYS}
            />
          </div>
        </div>
      </div>
    </Card>
  );
}
