import React, { useEffect, useRef, useState } from 'react';
import { SilkeAutocompleteField } from '../silke-autocomplete-field';
import { useSilkeCSSContext } from '../silke-css-number-field';
import { SilkeBox } from '../silke-box';
import { SilkeAutocompletePopup } from '../silke-autocomplete-field/silke-autocomplete-popup';
import { SilkeTextMicro, SilkeTextSmall } from '../silke-text';
import { SilkeButton } from '../silke-button';
import styles from './silke-variables-wrapper.scss';
import { useTextFieldContext } from '../silke-text-field/text-field-context';
import {
  getCSSNumberFormat,
  getRelevantVariables,
  getVariableDisplayText,
  getVariableIcon,
  getVariableUnit,
  getVariableValue,
  LocalVariable,
} from '@vev/utils';
import { SilkeIcon } from '../silke-icon';
import { capitalize } from 'lodash';
import { SilkeVariablesWrapperForm } from './silke-variables-wrapper-form';
import { SilkeTooltip } from '../silke-tooltip';

export type SilkeVariablesWrapperProps<T extends React.ElementType> = {
  component?: T;
  value: string;
  formats?: string[];
  variableType?: string;
  fullWidthWrapper?: boolean;
  templateVariable?: boolean;
  onChange: (value: string) => void; // return key?
  onVariableAdd?: (variable: LocalVariable) => Promise<string>;
  onVariableEdit?: (key: string) => void;
} & React.ComponentProps<T>;

const DROPDOWN_WIDTH = 189;
const SUPPORTED_TYPES = ['text', 'number', 'color', 'image', 'video', 'font'];

export function SilkeVariablesWrapper<T extends React.ElementType>({
  component,
  formats,
  variableType,
  fullWidthWrapper,
  templateVariable,
  onVariableAdd,
  onVariableEdit,
  ...rest
}: SilkeVariablesWrapperProps<T>) {
  const [openVariables, setOpenVariables] = useState(false);
  const [showAddNewForm, setShowAddNewForm] = useState(false);
  const [hovering, setHovering] = useState(false);

  if (!formats) formats = [];

  const rootRef = useRef<HTMLDivElement>(null);
  const context = useSilkeCSSContext();
  const textFieldContext = useTextFieldContext();
  const [hoveredItem, setHoveredItem] = useState<string | null>(null);

  const filteredVariables = React.useMemo(() => {
    return getRelevantVariables(context.variablesv2, formats, variableType);
  }, [context.variablesv2, formats, variableType]);

  const variableItems = React.useMemo(() => {
    return filteredVariables.map((v) => ({
      label: v.name,
      value: v.key,
      subLabel:
        v.type === 'font'
          ? context.getFontName?.(v.value || '') || ''
          : `${v.value}${v.unit ? `${v.unit}` : ''}`,
      icon: getVariableIcon(v.type),
    }));
  }, [filteredVariables]);

  const isVariable =
    typeof rest.value === 'string' &&
    (rest.value?.startsWith('var(--vev-') || rest.value?.startsWith('{{'));

  const currentVariable = React.useMemo(
    () => context.variablesv2.find((v) => typeof rest.value === 'string' && rest.value?.includes(v.key)),
    [context.variablesv2, rest.value],
  );

  const supportedType = !variableType || SUPPORTED_TYPES.includes(variableType || '');
  const displayValue = isVariable
    ? getVariableDisplayText(variableType, currentVariable, rest.value)
    : rest.value;

  const hasLabelTop =
    rest.label && rest.inline !== 'label-inside' && textFieldContext.inline !== 'label-inside';
  const offsetPx = rest.size === 's' || textFieldContext.size === 's' ? 16 : 18;

  useEffect(() => {
    const el = rootRef.current;
    if (el) {
      let timeout = 0;
      const handleMouseOver = () => {
        clearTimeout(timeout);
        timeout = self.setTimeout(() => setHovering(true), 300);
      };
      const handleMouseOut = () => {
        clearTimeout(timeout);
        setHovering(false);
      };
      el.addEventListener('mouseover', handleMouseOver, { passive: true });
      el.addEventListener('mouseleave', handleMouseOut, { passive: true });

      return () => {
        el.removeEventListener('mouseover', handleMouseOver);
        el.removeEventListener('mouseleave', handleMouseOver);
      };
    }
  }, []);

  const handleVariablesDotClicked = (e: React.MouseEvent) => {
    e.preventDefault();

    // If active variable, remove link and set to native value
    if (isVariable && currentVariable) {
      const variable = context.variablesv2.find((v) => rest.value?.includes(v.key));
      const wasGradient = variable?.type === 'color' && variable.value?.includes('gradient');

      if (wasGradient) {
        rest.onChange('000');
      } else {
        rest.onChange(variable ? `${variable.value}${variable.unit || ''}` : '0');
      }
      return;
    }
    if (filteredVariables.length) {
      setOpenVariables(true);
    } else {
      setShowAddNewForm(true);
    }
  };

  const onShowAddForm = () => {
    setOpenVariables(false);
    setShowAddNewForm(true);
  };

  const onHideAddForm = () => {
    setShowAddNewForm(false);
    setOpenVariables(true);
  };

  const handleVariableChange = (key: string) => {
    // Tracking (if implemented)
    const variable = filteredVariables.find((v) => v.key === key);
    context?.onVariableAttached?.(variable?.type || '');

    // Change the value
    return variableType === 'text' || templateVariable
      ? rest.onChange(`{{${key}}}`)
      : rest.onChange(`var(--vev-${key})`);
  };

  const handleVariableAdd = async ({ variableName }: { variableName: string }) => {
    const newVariable = {
      type: variableType,
      name: variableName ? variableName : capitalize(variableType),
      value: isVariable ? getVariableValue(rest.value, context.variablesv2) || '0' : rest.value,
      unit: '',
    };

    if (variableType === 'number') {
      newVariable.value = isVariable
        ? getVariableValue(rest.value, context.variablesv2) || '0'
        : String(parseFloat(rest.value || '0'));
      newVariable.unit = isVariable
        ? getVariableUnit(rest.value, context.variablesv2) || ''
        : getCSSNumberFormat(rest.value);
    }

    const newKey = await onVariableAdd?.(newVariable);

    if (newKey) {
      handleVariableChange(newKey);
    }

    setShowAddNewForm(false);
  };

  const disabled = 'disabled' in rest ? rest.disabled : false;
  if (disabled) return React.createElement(component, { ...rest, value: displayValue, formats });

  const itemRender = (item: any) => (
    <SilkeBox
      key={`${item.label}-${item.value}`}
      gap="s"
      hPad="s"
      vAlign="center"
      overflow="hidden"
      onMouseEnter={() => setHoveredItem(item.value)}
      onMouseLeave={() => setHoveredItem(null)}
      fill
    >
      <SilkeBox
        size="xs"
        hAlign
        vAlign
        rounded="tiny"
        style={variableType === 'color' ? { background: item.subLabel } : undefined}
      >
        {variableType !== 'color' && <SilkeIcon color="neutral-70" icon={item.icon} />}
      </SilkeBox>

      <SilkeBox column style={{ minWidth: '0', flex: 1 }}>
        <SilkeTextSmall overflow style={{ marginBottom: '-2px' }}>
          {item.label}
        </SilkeTextSmall>
        <SilkeTextMicro overflow color="neutral-60">
          {item.subLabel}
        </SilkeTextMicro>
      </SilkeBox>

      {hoveredItem === item.value && onVariableEdit && (
        <SilkeBox>
          <SilkeButton
            icon="settings"
            kind="ghost"
            size="s"
            onMouseDown={(e) => {
              e.preventDefault;
              e.stopPropagation();
              onVariableEdit?.(item.value);
            }}
          />
        </SilkeBox>
      )}
    </SilkeBox>
  );

  return (
    <div
      className={styles.wrapper}
      ref={rootRef}
      data-open={openVariables || showAddNewForm}
      data-is-variable={isVariable}
      data-full-width={fullWidthWrapper}
    >
      {supportedType && (
        <div
          className={styles.variableDot}
          data-icon={isVariable && currentVariable ? '−' : '+'}
          onClick={handleVariablesDotClicked}
          style={{ top: hasLabelTop ? offsetPx : 0 }}
        />
      )}
      {hovering && isVariable && currentVariable && (
        <SilkeTooltip show anchor={rootRef} anchorOrigin="top-center" targetOrigin="bottom-center">
          {currentVariable?.name}
        </SilkeTooltip>
      )}
      {isVariable && currentVariable ? (
        <SilkeAutocompleteField
          {...rest}
          isVariable
          items={variableItems}
          searchPlaceholder="Search variables"
          renderItem={({ item }) => itemRender(item)}
          value={displayValue}
          selectedVariableKey={currentVariable?.key}
          dropDownMinWidth={DROPDOWN_WIDTH}
          dropDownMaxWidth={DROPDOWN_WIDTH}
          width={rest.width}
          onChange={handleVariableChange}
          hideListArrow
          disableAddFromList
        />
      ) : (
        <div>
          {component && React.createElement(component, { ...rest, value: displayValue, formats })}
        </div>
      )}
      {openVariables && (
        <SilkeAutocompletePopup
          containerRef={rootRef}
          renderItem={({ item }) => itemRender(item)}
          dropDownMinWidth={DROPDOWN_WIDTH}
          dropDownMaxWidth={DROPDOWN_WIDTH}
          items={variableItems}
          value={currentVariable?.name}
          onAdd={onShowAddForm}
          onChange={handleVariableChange}
          onRequestClose={() => setOpenVariables(false)}
          showAddIcon={!!onVariableAdd}
          searchPlaceholder="Search variables"
          disableAddFromList
        />
      )}
      {showAddNewForm && (
        <SilkeVariablesWrapperForm
          containerRef={rootRef}
          variableValue={rest.value}
          onSubmit={handleVariableAdd}
          onClose={onHideAddForm}
        />
      )}
    </div>
  );
}

SilkeVariablesWrapper.displayName = 'SilkeVariablesWrapper';
