import React, { useCallback, useEffect, useState } from "react";
import type { GroupBase, Props, InputProps, SingleValue, MultiValue } from "react-select";
import ReactSelect, { components } from "react-select";

import classNames from "@calcom/lib/classNames";
import { useGetTheme } from "@calcom/lib/hooks/useTheme";

export type SelectProps<
  Option,
  IsMulti extends boolean = false,
  Group extends GroupBase<Option> = GroupBase<Option>
> = Props<Option, IsMulti, Group>;

export const InputComponent = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>({
  inputClassName,
  ...props
}: InputProps<Option, IsMulti, Group>) => {
  return (
    <components.Input
      // disables our default form focus hightlight on the react-select input element
      inputClassName={classNames("focus:ring-0 focus:ring-offset-0", inputClassName)}
      {...props}
    />
  );
};

function Select<
  Option,
  IsMulti extends boolean = false,
  Group extends GroupBase<Option> = GroupBase<Option>
>({ className, ...props }: SelectProps<Option, IsMulti, Group>) {
  const [mounted, setMounted] = useState<boolean>(false);
  const { resolvedTheme, forcedTheme } = useGetTheme();
  const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
  const darkThemeColors = {
    /** Dark Theme starts */
    //primary - Border when selected and Selected Option background
    primary: "rgb(41 41 41 / var(--tw-border-opacity))",

    neutral0: "rgb(62 62 62 / var(--tw-bg-opacity))",
    // Down Arrow  hover color
    neutral5: "white",

    neutral10: "rgb(41 41 41 / var(--tw-border-opacity))",

    // neutral20 - border color + down arrow default color
    neutral20: "rgb(41 41 41 / var(--tw-border-opacity))",

    // neutral30 - hover border color
    neutral30: "rgb(41 41 41 / var(--tw-border-opacity))",

    neutral40: "white",

    danger: "white",

    // Cross button in multiselect
    dangerLight: "rgb(41 41 41 / var(--tw-border-opacity))",

    // neutral50 - MultiSelect - "Select Text" color
    neutral50: "white",

    // neutral60 - Down Arrow color
    neutral60: "white",

    neutral70: "red",

    // neutral80 - Selected option
    neutral80: "white",

    neutral90: "blue",

    primary50: "rgba(209 , 213, 219, var(--tw-bg-opacity))",
    primary25: "rgba(244, 245, 246, var(--tw-bg-opacity))",
    /** Dark Theme ends */
  };

  useEffect(() => {
    setMounted(true);
  }, []);

  // Till we know in JS the theme is ready, we can't render react-select as it would render with light theme instead
  if (!mounted) {
    return <input type="text" className={className} />;
  }

  return (
    <ReactSelect
      theme={(theme) => ({
        ...theme,
        borderRadius: 6,
        colors: {
          ...theme.colors,
          ...(hasDarkTheme
            ? darkThemeColors
            : {
                /** Light Theme starts */
                primary: "var(--brand-color)",
                primary50: "rgba(209 , 213, 219, var(--tw-bg-opacity))",
                primary25: "rgba(244, 245, 246, var(--tw-bg-opacity))",
                /** Light Theme Ends */
              }),
        },
      })}
      styles={{
        option: (provided, state) => ({
          ...provided,
          color: state.isSelected ? "var(--brand-text-color)" : "black",
          ":active": {
            backgroundColor: state.isSelected ? "" : "var(--brand-color)",
            color: "var(--brand-text-color)",
          },
        }),
      }}
      components={{
        ...components,
        IndicatorSeparator: () => null,
        Input: InputComponent,
      }}
      className={classNames("border-0 text-sm", className)}
      {...props}
    />
  );
}

export function SelectWithValidation<
  Option extends { label: string; value: string },
  isMulti extends boolean = false,
  Group extends GroupBase<Option> = GroupBase<Option>
>({
  required = false,
  onChange,
  value,
  ...remainingProps
}: SelectProps<Option, isMulti, Group> & { required?: boolean }) {
  const [hiddenInputValue, _setHiddenInputValue] = useState(() => {
    if (value instanceof Array || !value) {
      return;
    }
    return value.value || "";
  });

  const setHiddenInputValue = useCallback((value: MultiValue<Option> | SingleValue<Option>) => {
    let hiddenInputValue = "";
    if (value instanceof Array) {
      hiddenInputValue = value.map((val) => val.value).join(",");
    } else {
      hiddenInputValue = value?.value || "";
    }
    _setHiddenInputValue(hiddenInputValue);
  }, []);

  useEffect(() => {
    if (!value) {
      return;
    }
    setHiddenInputValue(value);
  }, [value, setHiddenInputValue]);

  return (
    <div className={classNames("relative", remainingProps.className)}>
      <Select
        value={value}
        {...remainingProps}
        onChange={(value, ...remainingArgs) => {
          setHiddenInputValue(value);
          if (onChange) {
            onChange(value, ...remainingArgs);
          }
        }}
      />
      {required && (
        <input
          tabIndex={-1}
          autoComplete="off"
          style={{
            opacity: 0,
            width: "100%",
            height: 1,
            position: "absolute",
          }}
          value={hiddenInputValue}
          // eslint-disable-next-line @typescript-eslint/no-empty-function
          onChange={() => {}}
          // TODO:Not able to get focus to work
          // onFocus={() => selectRef.current?.focus()}
          required={required}
        />
      )}
    </div>
  );
}

export default Select;
