import React, { memo, useCallback, useEffect, useMemo, useState } from "react";
import Tooltip from "./Tooltip";
import { useSwatchSettings } from "../hooks/useSwatchSettings";

interface SwatchData {
  type: string;
  color: string;
  label: string;
  hide_label?: boolean;
}

type SwatchOption = {
  label: string;
  value: string;
  disabled: boolean;
};

interface SwatchGroupProps {
  attribute: string;
  select: HTMLSelectElement;
  swatches: Record<string, SwatchData>;
  settingsOverride?: Record<string, unknown>;
}

const SHAPE_CLASS = "b3-wvs-rounded-none";

const SwatchGroup: React.FC<SwatchGroupProps> = ({
  select,
  swatches,
  settingsOverride,
}) => {
  const [options, setOptions] = useState<SwatchOption[]>([]);
  const [selectedValue, setSelectedValue] = useState(select.value || "");

  const settings: Record<string, unknown> = useMemo(() => {
    if (settingsOverride && typeof settingsOverride === "object") {
      return settingsOverride;
    }

    return typeof b3WvsData !== "undefined" && b3WvsData?.settings
      ? b3WvsData.settings
      : {};
  }, [settingsOverride]);
  const {
    tooltipsEnabled,
    layoutPaddingTop,
    layoutPaddingRight,
    layoutPaddingBottom,
    layoutPaddingLeft,
    externalPaddingTop,
    externalPaddingRight,
    externalPaddingBottom,
    externalPaddingLeft,
    paddingTop,
    paddingRight,
    paddingBottom,
    paddingLeft,
    showLabelsColors,
    selectedColor,
    swatchWidth,
    swatchHeight,
    buttonSwatchWidth,
    buttonSwatchHeight,
    swatchGap,
    labelFontSize,
  } = useSwatchSettings(settings);

  useEffect(() => {
    let observer: MutationObserver | null = null;
    const form = select.closest(
      "form.variations_form",
    ) as HTMLFormElement | null;
    const jq =
      typeof window !== "undefined" ? (window as any).jQuery : undefined;
    const $form = form && typeof jq === "function" ? jq(form) : null;

    const triggerChange = () => {
      try {
        select.dispatchEvent(new Event("change", { bubbles: true }));
      } catch {
        const ev = document.createEvent("Event");
        ev.initEvent("change", true, true);
        select.dispatchEvent(ev);
      }
    };

    const updateOptions = () => {
      const nextOptions = Array.from(select.options).reduce<SwatchOption[]>(
        (acc, option) => {
          const value = option.value || "";
          if (!value) {
            return acc;
          }

          acc.push({
            label: (
              option.getAttribute("data-label") ||
              option.textContent ||
              value
            ).trim(),
            value,
            disabled: Boolean(option.disabled),
          });
          return acc;
        },
        [],
      );

      setOptions(nextOptions);

      const currentValue = select.value || "";
      if (
        currentValue !== "" &&
        !nextOptions.find((item) => item.value === currentValue)
      ) {
        select.value = "";
        triggerChange();
      }

      setSelectedValue(select.value || "");
    };

    const syncFromWooEvent = () => {
      window.setTimeout(() => {
        updateOptions();
      }, 0);
    };

    updateOptions();

    const handleChange = () => {
      setSelectedValue(select.value || "");
      updateOptions();
    };

    const handleResetClick = (event: Event) => {
      const target = event.target as HTMLElement | null;
      if (!target || !form) {
        return;
      }

      const resetLink = target.closest(".reset_variations");
      if (!resetLink || !form.contains(resetLink)) {
        return;
      }

      syncFromWooEvent();
    };

    select.addEventListener("change", handleChange);
    document.addEventListener("click", handleResetClick);

    if ($form && typeof $form.on === "function") {
      $form.on(
        "update_variation_values.b3wvs woocommerce_update_variation_values.b3wvs found_variation.b3wvs reset_data.b3wvs hide_variation.b3wvs",
        syncFromWooEvent,
      );
    }

    observer = new MutationObserver(updateOptions);
    observer.observe(select, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ["disabled", "value"],
    });

    return () => {
      select.removeEventListener("change", handleChange);
      document.removeEventListener("click", handleResetClick);
      if ($form && typeof $form.off === "function") {
        $form.off(
          "update_variation_values.b3wvs woocommerce_update_variation_values.b3wvs found_variation.b3wvs reset_data.b3wvs hide_variation.b3wvs",
          syncFromWooEvent,
        );
      }
      observer?.disconnect();
    };
  }, [select]);

  const layoutPadding = useMemo(
    () =>
      `${layoutPaddingTop}px ${layoutPaddingRight}px ${layoutPaddingBottom}px ${layoutPaddingLeft}px`,
    [
      layoutPaddingBottom,
      layoutPaddingLeft,
      layoutPaddingRight,
      layoutPaddingTop,
    ],
  );

  const outerPadding = useMemo(
    () =>
      `${externalPaddingTop}px ${externalPaddingRight}px ${externalPaddingBottom}px ${externalPaddingLeft}px`,
    [
      externalPaddingBottom,
      externalPaddingLeft,
      externalPaddingRight,
      externalPaddingTop,
    ],
  );

  const swatchPadding = useMemo(
    () =>
      `${paddingTop}px ${paddingRight}px ${paddingBottom}px ${paddingLeft}px`,
    [paddingBottom, paddingLeft, paddingRight, paddingTop],
  );

  const gapStyle = useMemo(() => ({ gap: `${swatchGap}px` }), [swatchGap]);

  const labelTextStyle = useMemo(() => {
    const labelTextColorOverride =
      typeof settings.label_text_color === "string"
        ? settings.label_text_color.trim()
        : "";

    return {
      fontSize: `${labelFontSize}px`,
      lineHeight: 1,
      ...(labelTextColorOverride ? { color: labelTextColorOverride } : {}),
    } as React.CSSProperties;
  }, [labelFontSize, settings.label_text_color]);

  const getTextColorForBackground = useCallback((bg: string | undefined) => {
    if (!bg) {
      return undefined;
    }

    const hex = bg.trim();
    const hexMatch = hex.match(/^#?([0-9a-fA-F]{6})$/);
    if (hexMatch) {
      const value = hexMatch[1];
      const r = parseInt(value.slice(0, 2), 16);
      const g = parseInt(value.slice(2, 4), 16);
      const b = parseInt(value.slice(4, 6), 16);
      const luminance = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
      return luminance < 0.55 ? "#ffffff" : "#111827";
    }

    return undefined;
  }, []);

  const handleSwatchClick = useCallback(
    (value: string, isDisabled: boolean) => {
      if (isDisabled) {
        return;
      }

      select.value = value;

      const jq =
        typeof window !== "undefined" ? (window as any).jQuery : undefined;
      if (typeof jq === "function") {
        jq(select).trigger("change");
      } else {
        try {
          select.dispatchEvent(new Event("change", { bubbles: true }));
        } catch {
          const event = document.createEvent("Event");
          event.initEvent("change", true, true);
          select.dispatchEvent(event);
        }
      }

      setSelectedValue(value);
    },
    [select],
  );

  const renderTooltip = useCallback(
    (text: string | undefined, child: React.ReactElement, key: string) => (
      <Tooltip key={key} text={text} enabled={tooltipsEnabled} position="top">
        {child}
      </Tooltip>
    ),
    [tooltipsEnabled],
  );

  const renderSwatch = useCallback(
    (option: SwatchOption) => {
      const data = swatches[option.value];
      const isDisabled = option.disabled;
      const isSelected = selectedValue === option.value;
      const isColor = data?.type === "color";
      const shouldShowLabel = isColor
        ? Boolean(showLabelsColors && !data?.hide_label)
        : true;
      const label = data?.label || option.label;
      const hasColor = typeof data?.color === "string" && data.color.trim();
      const showSelectedIndicator = isSelected;

      if (isColor) {
        return renderTooltip(
          label,
          <div className="b3-wvs-relative b3-wvs-flex b3-wvs-flex-col b3-wvs-items-center">
            <div className="b3-wvs-relative">
              <button
                type="button"
                disabled={isDisabled}
                aria-pressed={isSelected}
                aria-label={label}
                data-b3-wvs-swatch
                onClick={() => handleSwatchClick(option.value, isDisabled)}
                className={`b3-wvs-relative b3-wvs-flex b3-wvs-items-center b3-wvs-justify-center b3-wvs-border b3-wvs-border-transparent b3-wvs-transition-all ${SHAPE_CLASS} ${
                  isDisabled
                    ? "b3-wvs-opacity-40 b3-wvs-cursor-not-allowed"
                    : "b3-wvs-cursor-pointer"
                }`}
                style={
                  {
                    "--b3-wvs-swatch-height": `${swatchHeight}px`,
                    "--b3-wvs-swatch-padding": swatchPadding,
                    width: `${swatchWidth}px`,
                    height: `${swatchHeight}px`,
                    padding: swatchPadding,
                    boxSizing: "border-box",
                    backgroundColor: data?.color || "",
                  } as React.CSSProperties
                }
                onKeyDown={(event) => {
                  if (isDisabled) {
                    return;
                  }

                  if (event.key === "Enter" || event.key === " ") {
                    event.preventDefault();
                    handleSwatchClick(option.value, isDisabled);
                  }
                }}
              />

              {showSelectedIndicator ? (
                <div
                  className="b3-wvs-absolute b3-wvs-left-1/2 b3-wvs--translate-x-1/2 b3-wvs-bottom-[-6px] b3-wvs-w-6 b3-wvs-h-1 b3-wvs-rounded-full"
                  style={{ backgroundColor: selectedColor }}
                />
              ) : null}
            </div>
            {shouldShowLabel ? (
              <span
                className="b3-wvs-mt-2 b3-wvs-block b3-wvs-text-center b3-wvs-whitespace-nowrap"
                style={labelTextStyle}
                data-b3-wvs-swatch-text
              >
                {label}
              </span>
            ) : null}
          </div>,
          option.value,
        );
      }

      return renderTooltip(
        label,
        <div className="b3-wvs-relative b3-wvs-flex b3-wvs-flex-col b3-wvs-items-center">
          <div className="b3-wvs-relative">
            <button
              type="button"
              disabled={isDisabled}
              aria-pressed={isSelected}
              aria-label={shouldShowLabel ? undefined : label}
              data-b3-wvs-swatch
              onClick={() => handleSwatchClick(option.value, isDisabled)}
              className={`b3-wvs-relative b3-wvs-inline-flex b3-wvs-items-center b3-wvs-justify-center b3-wvs-leading-none b3-wvs-whitespace-nowrap b3-wvs-font-medium b3-wvs-transition-all ${SHAPE_CLASS} ${
                isDisabled
                  ? "b3-wvs-opacity-40 b3-wvs-cursor-not-allowed"
                  : "b3-wvs-cursor-pointer"
              } ${hasColor ? "b3-wvs-border b3-wvs-border-transparent" : "b3-wvs-border-b-4 b3-wvs-border-transparent"}`}
              style={
                {
                  "--b3-wvs-swatch-height": `${buttonSwatchHeight}px`,
                  "--b3-wvs-swatch-padding": swatchPadding,
                  minWidth: `${buttonSwatchWidth}px`,
                  height: `${buttonSwatchHeight}px`,
                  padding: swatchPadding,
                  boxSizing: "border-box",
                  ...(hasColor
                    ? {
                        backgroundColor: data.color,
                        color: getTextColorForBackground(data.color),
                      }
                    : {}),
                } as React.CSSProperties
              }
              onKeyDown={(event) => {
                if (isDisabled) {
                  return;
                }

                if (event.key === "Enter" || event.key === " ") {
                  event.preventDefault();
                  handleSwatchClick(option.value, isDisabled);
                }
              }}
            >
              {shouldShowLabel ? (
                <span
                  className="b3-wvs-block b3-wvs-text-center b3-wvs-whitespace-nowrap"
                  style={labelTextStyle}
                  data-b3-wvs-swatch-text
                >
                  {label}
                </span>
              ) : null}
            </button>

            {showSelectedIndicator ? (
              <div
                className="b3-wvs-absolute b3-wvs-left-1/2 b3-wvs--translate-x-1/2 b3-wvs-bottom-[-6px] b3-wvs-w-4/5 b3-wvs-h-1 b3-wvs-rounded-full"
                style={{ backgroundColor: selectedColor }}
              />
            ) : null}
          </div>
        </div>,
        option.value,
      );
    },
    [
      buttonSwatchHeight,
      buttonSwatchWidth,
      getTextColorForBackground,
      handleSwatchClick,
      labelTextStyle,
      renderTooltip,
      selectedColor,
      selectedValue,
      showLabelsColors,
      swatchHeight,
      swatchPadding,
      swatchWidth,
      swatches,
    ],
  );

  return (
    <div style={{ padding: outerPadding }}>
      <div
        className="b3-wvs-flex b3-wvs-flex-wrap b3-wvs-items-center"
        style={{ padding: layoutPadding, ...gapStyle }}
      >
        {options.map((option) => (
          <div key={option.value}>{renderSwatch(option)}</div>
        ))}
      </div>
    </div>
  );
};

export default memo(SwatchGroup);
