import { Button } from "@/components/Button";
import {
  ArrayHelpers,
  ErrorMessage,
  Field,
  FieldArray,
  useField,
  useFormikContext,
} from "formik";
import * as Yup from "yup";
import { FormBodyBuilder } from "./types";
import { FC, useEffect, useRef, useState } from "react";
import { ChevronDownIcon } from "@/components/icons/icons";

interface TextFormFieldProps {
  name: string;
  label: string;
  subtext?: string;
  placeholder?: string;
  type?: string;
  disabled?: boolean;
  autoCompleteDisabled?: boolean;
}

export const TextFormField = ({
  name,
  label,
  subtext,
  placeholder,
  type = "text",
  disabled = false,
  autoCompleteDisabled = false,
}: TextFormFieldProps) => {
  return (
    <div className="mb-4">
      <label htmlFor={name} className="block font-medium">
        {label}
      </label>
      {subtext && <p className="text-xs mb-1">{subtext}</p>}
      <Field
        type={type}
        name={name}
        id={name}
        className={
          `
          border 
          text-gray-200 
          border-gray-300 
          rounded 
          w-full 
          py-2 
          px-3 
          mt-1
        ` + (disabled ? " bg-slate-900" : " bg-slate-700")
        }
        disabled={disabled}
        placeholder={placeholder}
        autoComplete={autoCompleteDisabled ? "off" : undefined}
      />
      <ErrorMessage
        name={name}
        component="div"
        className="text-red-500 text-sm mt-1"
      />
    </div>
  );
};

interface BooleanFormFieldProps {
  name: string;
  label: string;
  subtext?: string;
}

export const BooleanFormField = ({
  name,
  label,
  subtext,
}: BooleanFormFieldProps) => {
  return (
    <div className="mb-4">
      <label className="flex text-sm">
        <Field name={name} type="checkbox" className="mx-3 px-5" />
        <div>
          <p className="font-medium">{label}</p>
          {subtext && <p className="text-xs">{subtext}</p>}
        </div>
      </label>

      <ErrorMessage
        name={name}
        component="div"
        className="text-red-500 text-sm mt-1"
      />
    </div>
  );
};

interface TextArrayFieldProps<T extends Yup.AnyObject> {
  name: string;
  label: string | JSX.Element;
  values: T;
  subtext?: string | JSX.Element;
  type?: string;
}

export function TextArrayField<T extends Yup.AnyObject>({
  name,
  label,
  values,
  subtext,
  type,
}: TextArrayFieldProps<T>) {
  return (
    <div className="mb-4">
      <label htmlFor={name} className="block font-medium">
        {label}
      </label>
      {subtext && <p className="text-xs">{subtext}</p>}

      <FieldArray
        name={name}
        render={(arrayHelpers: ArrayHelpers) => (
          <div>
            {values[name] &&
              values[name].length > 0 &&
              (values[name] as string[]).map((_, index) => (
                <div key={index} className="mt-2">
                  <div className="flex">
                    <Field
                      type={type}
                      name={`${name}.${index}`}
                      id={name}
                      className="border bg-slate-700 text-gray-200 border-gray-300 rounded w-full py-2 px-3 mr-2"
                      // Disable autocomplete since the browser doesn't know how to handle an array of text fields
                      autoComplete="off"
                    />
                    <Button
                      type="button"
                      onClick={() => arrayHelpers.remove(index)}
                      className="h-8 my-auto"
                    >
                      Remove
                    </Button>
                  </div>
                  <ErrorMessage
                    name={`${name}.${index}`}
                    component="div"
                    className="text-red-500 text-sm mt-1"
                  />
                </div>
              ))}
            <Button
              type="button"
              onClick={() => {
                arrayHelpers.push("");
              }}
              className="mt-3"
            >
              Add New
            </Button>
          </div>
        )}
      />
    </div>
  );
}

interface TextArrayFieldBuilderProps<T extends Yup.AnyObject> {
  name: string;
  label: string;
  subtext?: string;
  type?: string;
}

export function TextArrayFieldBuilder<T extends Yup.AnyObject>(
  props: TextArrayFieldBuilderProps<T>
): FormBodyBuilder<T> {
  const _TextArrayField: FormBodyBuilder<T> = (values) => (
    <TextArrayField {...props} values={values} />
  );
  return _TextArrayField;
}

interface Option {
  name: string;
  value: string;
  description?: string;
}

interface SelectorFormFieldProps {
  name: string;
  label: string;
  options: Option[];
  subtext?: string;
}

export function SelectorFormField({
  name,
  label,
  options,
  subtext,
}: SelectorFormFieldProps) {
  const [field] = useField<string>(name);
  const { setFieldValue } = useFormikContext();

  return (
    <div className="mb-4">
      <label className="flex mb-2">
        <div>
          {label}
          {subtext && <p className="text-xs">{subtext}</p>}
        </div>
      </label>

      <Dropdown
        options={options}
        selected={field.value}
        onSelect={(selected) => setFieldValue(name, selected.value)}
      />

      <ErrorMessage
        name={name}
        component="div"
        className="text-red-500 text-sm mt-1"
      />
    </div>
  );
}

interface DropdownProps {
  options: Option[];
  selected: string;
  onSelect: (selected: Option) => void;
}

const Dropdown: FC<DropdownProps> = ({ options, selected, onSelect }) => {
  const [isOpen, setIsOpen] = useState(false);
  const dropdownRef = useRef<HTMLDivElement>(null);

  const selectedName = options.find(
    (option) => option.value === selected
  )?.name;

  const handleSelect = (option: Option) => {
    onSelect(option);
    setIsOpen(false);
  };

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (
        dropdownRef.current &&
        !dropdownRef.current.contains(event.target as Node)
      ) {
        setIsOpen(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, []);

  return (
    <div className="relative inline-block text-left w-full" ref={dropdownRef}>
      <div>
        <button
          type="button"
          className={`inline-flex 
          justify-center 
          w-full 
          px-4 
          py-3
          text-sm 
          bg-gray-700 
          border 
          border-gray-300 
          rounded-md 
          shadow-sm 
          hover:bg-gray-700 
          focus:ring focus:ring-offset-0 focus:ring-1 focus:ring-offset-gray-800 focus:ring-blue-800
          `}
          id="options-menu"
          aria-expanded="true"
          aria-haspopup="true"
          onClick={() => setIsOpen(!isOpen)}
        >
          {selectedName ? <p>{selectedName}</p> : "Select an option..."}
          <ChevronDownIcon className="text-gray-400 my-auto ml-auto" />
        </button>
      </div>

      {isOpen ? (
        <div className="origin-top-right absolute left-0 mt-3 w-full rounded-md shadow-lg bg-gray-700 border-2 border-gray-600">
          <div
            role="menu"
            aria-orientation="vertical"
            aria-labelledby="options-menu"
          >
            {options.map((option, index) => (
              <button
                key={index}
                onClick={() => handleSelect(option)}
                className={
                  `w-full text-left block px-4 py-2.5 text-sm hover:bg-gray-800` +
                  (index !== 0 ? " border-t-2 border-gray-600" : "")
                }
                role="menuitem"
              >
                <p className="font-medium">{option.name}</p>
                {option.description && (
                  <div>
                    <p className="text-xs text-gray-300">
                      {option.description}
                    </p>
                  </div>
                )}
              </button>
            ))}
          </div>
        </div>
      ) : null}
    </div>
  );
};