import {
  Select as BaseSelect,
  SelectItem,
  SelectProps as BaseSelectProps,
  SelectSection,
  Selection,
  Chip,
  SelectedItems,
} from "@nextui-org/react";
import { ControllerFieldState } from "react-hook-form";
import { Key, useEffect, useState } from "react";
import { FaAsterisk } from "react-icons/fa6";

import { Options } from "@/types/options/options-data-types";

type ListOfItems = {
  data: Options;
  fieldProps: any;
  fieldState: ControllerFieldState;
  isClient?: boolean;
  isClearable?: boolean;
};

type ListOfGroups = {
  sections?: {
    title: string;
    category: string;
  }[];
};

type SelectProps = Omit<BaseSelectProps, "children"> &
  ListOfItems &
  ListOfGroups & {
    mapCategory?: (data: string) => string;
    control?: any;
    name?: string;
  };

function Select({
  data,
  sections,
  mapCategory,
  selectionMode = "single",
  disabledKeys,
  defaultSelectedKeys,
  variant = "faded",
  color,
  size,
  placeholder,
  label,
  isRequired = true,
  isDisabled = false,
  description,
  startContent,
  endContent,
  scrollRef,
  isClient,
  isClearable = true,
  classNames = {
    base: "light",
    trigger: "h-auto",
    label:
      "text-start block text-sm font-bold text-black tracking-tight font-inter text-pretty",
    mainWrapper: "",
    innerWrapper: "",
    selectorIcon: "text-black",
    value: "font-noto",
    listboxWrapper: "",
    listbox: "font-noto",
    popoverContent: "",
    helperWrapper: "",
    description:
      "font-inter font-regular text-[12px] text-gray-500 leading-tight",
  },
  fieldProps,
  fieldState,
}: SelectProps) {
  const [selectedElements, setSelectedElements] = useState<
    Selection | string | undefined
  >(() => {
    if (!fieldProps.value || fieldProps.value === 0) {
      return undefined; // this is required for cases where the id is 0. otherwise, the placeholder will not appear.
    }

    return selectionMode === "single"
      ? fieldProps.value.toString()
      : fieldProps.value && fieldProps.value.length > 0
      ? new Set(fieldProps.value.map((value: number) => value.toString()))
      : new Set();
  });

  const hasSections = sections && sections.length > 0;

  const handleOnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const selectedValue = e.target.value;

    if (!selectedValue) {
      setSelectedElements("0");
      fieldProps.onChange(0);
      return;
    }

    const newValues =
      selectionMode === "single"
        ? selectedValue
        : new Set(selectedValue.split(","));

    setSelectedElements(newValues);

    const arrayOfValues = Array.from(newValues).map((value) =>
      typeof value === "string" ? Number(value) : value
    );

    if (selectionMode === "single") {
      fieldProps.onChange(
        selectedValue ? Number(selectedValue) : selectedValue
      );
    } else {
      fieldProps.onChange(arrayOfValues.filter((value) => !isNaN(value)));
    }
  };

  const handleChipClose = (key: Key) => {
    setSelectedElements((prev) => {
      if (typeof prev === "string") {
        return prev;
      } else if (prev instanceof Set) {
        const newSet = new Set(
          Array.from(prev).filter((itemKey) => itemKey !== key)
        );
        return newSet;
      }
    });

    if (selectedElements instanceof Set) {
      const newSet = new Set(
        Array.from(selectedElements).filter((itemKey) => itemKey !== key)
      );

      fieldProps.onChange(Array.from(newSet).map(Number));
    }
  };

  // This useEffect hook is used to initialize the fieldProps.value to 0 if it's null, undefined or 0.
  // This is required to ensure that when the page is reloaded and the API returns 0 (indicating no value is selected),
  // the select component will still send 0 when submitted, even if the user doesn't interact with it.
  useEffect(() => {
    if (
      fieldProps.value === null ||
      fieldProps.value === undefined ||
      fieldProps.value === 0
    ) {
      fieldProps.onChange(0);
    }
  }, [fieldProps]);

  return (
    <div className="relative">
      <span
        className="text-sm font-bold text-black tracking-tight font-inter pb-2 text-ellipsis"
        id={`${fieldProps?.name}-label`}
      >
        {label}{" "}
        {isRequired && (
          <>
            <FaAsterisk
              aria-hidden
              className="text-red-600 inline-block size-2 mb-2"
              focusable="false"
            />
            <span className="sr-only">required</span>
          </>
        )}
      </span>

      {/* Container for the Select and Clear button */}
      <div className="relative w-full">
        <BaseSelect
          {...fieldProps}
          isMultiline
          aria-label={fieldProps?.name}
          classNames={classNames}
          color={color}
          defaultSelectedKeys={defaultSelectedKeys}
          description={description}
          disabledKeys={disabledKeys}
          endContent={endContent}
          id={fieldProps?.name}
          isDisabled={isDisabled}
          isInvalid={fieldState.invalid ?? false}
          isRequired={isRequired}
          isVirtualized={false}
          items={data}
          placeholder={placeholder}
          radius="sm"
          renderValue={(items: SelectedItems<Options>) => {
            if (selectionMode === "multiple") {
              return (
                <div className="flex flex-wrap gap-2">
                  {items.map((item) => (
                    <Chip
                      key={item.key}
                      className="font-noto text-sm"
                      classNames={{
                        base: `${
                          isClient
                            ? "bg-paleBlue/40 text-black px-2"
                            : "bg-slate-300/80 text-black px-2"
                        }`,
                      }}
                      variant="light"
                      onClose={() => {
                        if (item.key !== undefined) {
                          handleChipClose(item.key);
                        }
                      }}
                    >
                      {item.props?.children}
                    </Chip>
                  ))}
                </div>
              );
            } else {
              return items.map((item) => item.props?.children);
            }
          }}
          scrollRef={scrollRef}
          selectedKeys={
            selectionMode === "single" && selectedElements
              ? [selectedElements]
              : selectedElements
          }
          selectionMode={selectionMode}
          size={size}
          startContent={startContent}
          value={fieldProps.value}
          variant={variant}
          onChange={handleOnChange}
        >
          {hasSections &&
            mapCategory &&
            sections.map((section) => (
              <SelectSection
                key={section.category}
                showDivider
                title={section.title}
              >
                {data
                  .filter(
                    (item) => mapCategory(item.title) === section.category
                  )
                  .map((item) => (
                    <SelectItem key={item.id} value={item.id}>
                      {item.title}
                    </SelectItem>
                  ))}
              </SelectSection>
            ))}
          {!hasSections &&
            data.map((item) => (
              <SelectItem key={item.id} value={item.id}>
                {item.title}
              </SelectItem>
            ))}
        </BaseSelect>
      </div>

      {isClearable &&
        selectedElements !== "0" &&
        selectedElements !== undefined &&
        selectionMode === "single" && (
          <button
            className="absolute flex items-center right-0 top-0 font-noto text-xs underline decoration-dotted underline-offset-2 underline-thickness-thin font-medium text-black focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black"
            type="button"
            onClick={() => {
              setSelectedElements("0");
              fieldProps.onChange(0);
            }}
          >
            Clear
          </button>
        )}
    </div>
  );
}

export default Select;
