"use client";

import { chevronDown } from "@simplybusiness/icons";
import classNames from "classnames/dedupe";
import type { ChangeEvent, ReactElement, RefAttributes } from "react";
import { useId } from "react";
import { useValidationClasses } from "../../hooks";
import type { UseLabelProps } from "../../hooks/useLabel";
import { useLabel } from "../../hooks/useLabel";
import type { DOMProps, Validation } from "../../types";
import { spaceDelimitedList } from "../../utils/spaceDelimitedList";
import { ErrorMessage } from "../ErrorMessage";
import { Icon } from "../Icon";
import { Label } from "../Label";
import type { OptionProps } from "../Option";
import { Stack } from "../Stack";
import "./Select.css";

export type SelectElementType = HTMLSelectElement;
export interface SelectProps
  extends
    UseLabelProps,
    Validation,
    DOMProps,
    RefAttributes<SelectElementType> {
  name?: string;
  onChange?: (e: ChangeEvent<HTMLSelectElement>) => void;
  value?: string;
  defaultValue?: string;
  className?: string;
  errorMessage?: string;
  children?: ReactElement<OptionProps>[] | ReactElement<OptionProps>;
  "aria-describedby"?: string;
  isDisabled?: boolean;
}

const Select = ({ ref, ...props }: SelectProps) => {
  const {
    label,
    onChange,
    isInvalid,
    errorMessage,
    isDisabled = false,
    isRequired,
    ...otherProps
  } = props;

  const { labelProps, fieldProps } = useLabel({
    label,
    ...otherProps,
  });

  const validationClasses = useValidationClasses({ isInvalid });

  const stateClasses = {
    "--is-disabled": isDisabled,
    "--is-required": typeof isRequired === "boolean" && isRequired,
    "--is-optional": typeof isRequired === "boolean" && !isRequired,
  };

  const sharedClasses = classNames(validationClasses, stateClasses);

  const wrapperClasses = classNames(
    "mobius-select__wrapper",
    sharedClasses,
    otherProps.className,
  );
  const selectClasses = classNames(
    "mobius-select",
    sharedClasses,
    otherProps.className,
  );
  const labelClasses = classNames(
    "mobius-label",
    sharedClasses,
    otherProps.className,
  );
  const iconClasses = classNames("mobius-select__icon", sharedClasses);
  const errorMessageId = useId();
  const shouldErrorMessageShow = errorMessage ? errorMessageId : undefined;
  const describedBy = spaceDelimitedList([
    shouldErrorMessageShow,
    props["aria-describedby"],
  ]);

  const handleChange = (e: ChangeEvent<HTMLSelectElement>) => {
    if (onChange) {
      onChange(e);
    }
  };

  return (
    <Stack className="mobius mobius-select__outer" gap="xs">
      {label && (
        <Label {...labelProps} className={labelClasses}>
          {label}
        </Label>
      )}
      <div className={wrapperClasses}>
        <select
          {...otherProps}
          {...fieldProps}
          ref={ref}
          multiple={false}
          className={selectClasses}
          disabled={isDisabled}
          aria-invalid={!!errorMessage}
          aria-describedby={describedBy}
          aria-required={isRequired}
          required={isRequired}
          onChange={handleChange}
        />
        <span className={iconClasses}>
          <Icon icon={chevronDown} />
        </span>
      </div>
      {errorMessage && (
        <ErrorMessage id={errorMessageId} errorMessage={errorMessage} />
      )}
    </Stack>
  );
};

Select.displayName = "Select";
export { Select };
