import * as React from "react";
import * as styles from "./select.module.css";
import clsx from "clsx";
import CreatableSelect from "react-select/creatable";
import ReactSelect, { MenuPlacement, StylesConfig, GroupBase, components } from "react-select";
import { Control, Controller, FieldValues } from "react-hook-form";
import { IReactHookFormProps } from "../types";
import { ErrorMessage } from "../errorMessage/ErrorMessage";

export type TSelectOption = { label: string; value: string };
export type TGroupedSelectOption = { label: string; options: TSelectOption[] };

interface ISelectProps {
  control: Control<any, any>;
  options: TSelectOption[] | TGroupedSelectOption[];
  name: string;
  ariaLabel: string;
  id?: string;
  defaultValue?: any;
  disabled?: boolean;
  isClearable?: boolean;
  hideErrorMessage?: boolean;
  menuPlacement?: MenuPlacement;
  placeholder?: string;
  clearIndicatorAttributes?: Record<string, string>;
}

const selectStyles: StylesConfig = {
  menuPortal: (base) => ({ ...base, zIndex: 100 }),
  option: (base, state) => ({
    ...base,
    fontFamily: `var(--conduction-input-select-list-option-font-family, ${base.fontFamily})`,
    backgroundColor: [
      state.isFocused
        ? `var(--conduction-input-select-list-option-focus-background-color, ${base.backgroundColor})`
        : state.isSelected
        ? `var(--conduction-input-select-list-option-selected-background-color, ${base.backgroundColor})`
        : `var(--conduction-input-select-list-option-background-color, ${base.backgroundColor})`,
    ],

    color: [
      state.isFocused
        ? `var(--conduction-input-select-list-option-focus-color, ${base.color})`
        : state.isSelected
        ? `var(--conduction-input-select-list-option-selected-color, ${base.color})`
        : `var(--conduction-input-select-list-option-color, ${base.color})`,
    ],

    "&:hover": {
      backgroundColor: `var(--conduction-input-select-list-option-hover-background-color, ${base.backgroundColor})`,
      color: `var(--conduction-input-select-list-option-hover-color, ${base.color})`,
      fontFamily: `var(--conduction-input-select-list-option-hover-font-family, var(--conduction-input-select-list-option-font-family, ${base.fontFamily}))`,
    },
  }),
  placeholder: (base) => ({
    ...base,
    fontFamily: `var(--conduction-input-select-placeholder-font-family, var(--utrecht-form-input-placeholder-font-family, ${base.fontFamily}))`,
    color: `var(--conduction-input-select-placeholder-color, var(--utrecht-form-input-placeholder-color, ${base.color}) )`,
  }),
  dropdownIndicator: (base) => ({
    ...base,
    color: "#949494",
    "&:hover": {
      color: "#949494",
    },
  }),
};

const setAttributes = (
  root: HTMLElement | Document = document,
  clearIndicatorAttributes?: Record<string, string>,
): void => {
  const setRoleToPresentation = (selector: string, role: string) => {
    root.querySelectorAll(selector).forEach((element) => {
      const currentRole = element.getAttribute("role");
      if (currentRole !== "presentation" && currentRole !== "listbox") element.setAttribute("role", role);
    });
  };

  const updateIndicatorAttributes = (indicator: HTMLElement, isInteractive: boolean) => {
    if (isInteractive) {
      indicator.setAttribute("role", "button");
      indicator.setAttribute("tabindex", "0");
      indicator.setAttribute("aria-label", "Clear selection");
      if (clearIndicatorAttributes) {
        Object.entries(clearIndicatorAttributes).forEach(([key, value]) => {
          indicator.setAttribute(key, value);
        });
      }
    } else {
      indicator.setAttribute("role", "presentation");
      indicator.removeAttribute("tabindex");
      indicator.removeAttribute("aria-label");
    }
  };

  const setAriaLabelsForIndicators = () => {
    root.querySelectorAll('[class*="control"]').forEach((control) => {
      const indicatorsParent = control.querySelector('[class*="indicatorSeparator"]')?.parentElement;
      if (!indicatorsParent) return;

      const indicators = indicatorsParent.querySelectorAll('[class*="indicatorContainer"]');
      const hasSelection = indicators.length === 2;

      indicators.forEach((indicator, index) => {
        const isClearButton = hasSelection && index === 0;
        updateIndicatorAttributes(indicator as HTMLElement, isClearButton);
      });
    });
  };

  // Initial static setup
  setRoleToPresentation('[class*="indicatorSeparator"]', "separator");

  // Dynamic setup after render
  setTimeout(() => {
    setAriaLabelsForIndicators();

    const observer = new MutationObserver(setAriaLabelsForIndicators);

    root.querySelectorAll('[class*="control"]').forEach((control) => {
      const indicatorsParent = control.querySelector('[class*="indicatorSeparator"]')?.parentElement;
      if (indicatorsParent) {
        observer.observe(indicatorsParent, { childList: true, subtree: false });
      }
    });
  }, 100);
};

// Custom ClearIndicator component that handles keyboard events for accessibility
const ClearIndicator = (props: any) => {
  const { clearValue, innerProps, children } = props;

  const handleKeyDown = (event: React.KeyboardEvent) => {
    if (event.key === " " || event.key === "Enter") {
      event.preventDefault();
      event.stopPropagation();
      if (clearValue) {
        clearValue();
      }
      if (innerProps?.onClick) {
        innerProps.onClick(event);
      }
    } else if (innerProps?.onKeyDown) {
      innerProps.onKeyDown(event);
    }
  };

  return (
    <components.ClearIndicator
      {...props}
      innerProps={{
        ...innerProps,
        onKeyDown: handleKeyDown,
      }}
    >
      {children}
    </components.ClearIndicator>
  );
};

export const SelectMultiple = ({
  id,
  name,
  options,
  errors,
  control,
  validation,
  defaultValue,
  disabled,
  hideErrorMessage,
  menuPlacement,
  placeholder,
  ariaLabel,
  clearIndicatorAttributes,
}: ISelectProps & IReactHookFormProps): JSX.Element => {
  const containerRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    if (containerRef.current) {
      setAttributes(containerRef.current, clearIndicatorAttributes);
    }
  }, []);
  return (
    <div ref={containerRef}>
      <Controller
        {...{ control, name, defaultValue }}
        rules={validation}
        render={({ field: { onChange, value } }) => {
          return (
            <>
              <ReactSelect
                aria-label={ariaLabel}
                inputId={id}
                value={value ?? ""}
                className={clsx(styles.select, errors[name] && styles.error)}
                isMulti
                isDisabled={disabled}
                {...{ options, onChange, errors }}
                menuPortalTarget={document.body}
                menuPlacement={menuPlacement}
                styles={selectStyles}
                placeholder={disabled ? "Disabled..." : placeholder ?? "Select one or more options..."}
                formatGroupLabel={(group) => <GroupLabel {...{ group }} />}
              />
              {errors[name] && !hideErrorMessage && <ErrorMessage message={errors[name]?.message as string} />}
            </>
          );
        }}
      />
    </div>
  );
};

export const SelectCreate = ({
  id,
  name,
  options,
  errors,
  control,
  validation,
  defaultValue,
  disabled,
  hideErrorMessage,
  menuPlacement,
  placeholder,
  ariaLabel,
  clearIndicatorAttributes,
}: ISelectProps & IReactHookFormProps): JSX.Element => {
  const containerRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    if (containerRef.current) {
      setAttributes(containerRef.current, clearIndicatorAttributes);
    }
  }, []);
  return (
    <div ref={containerRef}>
      <Controller
        {...{ control, name, defaultValue }}
        rules={validation}
        render={({ field: { onChange, value } }) => {
          return (
            <>
              <CreatableSelect
                aria-label={ariaLabel}
                inputId={id}
                value={value ?? ""}
                placeholder={disabled ? "Disabled..." : placeholder ?? "Select one or more options..."}
                className={clsx(styles.select, errors[name] && styles.error)}
                isMulti
                isDisabled={disabled}
                {...{ options, onChange, errors }}
                menuPortalTarget={document.body}
                menuPlacement={menuPlacement}
                styles={selectStyles}
                formatGroupLabel={(group) => <GroupLabel {...{ group }} />}
              />
              {errors[name] && !hideErrorMessage && <ErrorMessage message={errors[name]?.message as string} />}
            </>
          );
        }}
      />
    </div>
  );
};

export const SelectSingle = ({
  id,
  name,
  options,
  errors,
  control,
  validation,
  isClearable,
  defaultValue,
  disabled,
  hideErrorMessage,
  menuPlacement,
  placeholder,
  ariaLabel,
  clearIndicatorAttributes,
}: ISelectProps & IReactHookFormProps): JSX.Element => {
  const containerRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    if (containerRef.current) {
      setAttributes(containerRef.current, clearIndicatorAttributes);
    }
  }, []);
  return (
    <div ref={containerRef}>
      <Controller
        {...{ control, name, defaultValue }}
        rules={validation}
        render={({ field: { onChange, value } }) => {
          return (
            <>
              <ReactSelect
                aria-label={ariaLabel}
                inputId={id}
                value={value ?? ""}
                className={clsx(styles.select, errors[name] && styles.error)}
                isDisabled={disabled}
                {...{ options, onChange, errors, isClearable }}
                menuPortalTarget={document.body}
                menuPlacement={menuPlacement}
                styles={selectStyles}
                placeholder={disabled ? "Disabled..." : placeholder ?? "Select one or more options..."}
                formatGroupLabel={(group) => <GroupLabel {...{ group }} />}
                components={isClearable ? { ClearIndicator } : undefined}
              />
              {errors[name] && !hideErrorMessage && <ErrorMessage message={errors[name]?.message as string} />}
            </>
          );
        }}
      />
    </div>
  );
};

const GroupLabel: React.FC<{ group: GroupBase<unknown> }> = ({ group }) => {
  if (!group.label) return <></>;

  return <span className={styles.groupLabel}>{group.label}</span>;
};
