import { cn } from "@/lib/utils";
import { FormControl, FormDescription, FormItem, InputProps } from "@/ui";

import { Quantity, QuantityObject } from "@shared/contexts";
import { createElement, forwardRef, useState } from "react";
import {
  Controller,
  FieldPath,
  FieldValues,
  UseControllerProps,
  useFormContext,
} from "react-hook-form";
import { FormErrorMessage } from "./FormErrorMessage";
import { FormLabel, FormLabelProps } from "./FormLabel";
import { FormInputProps, FormInputWithIconProps } from "./FormProps";

export type FormQuantityFieldProps<
  TFieldValues extends FieldValues = FieldValues,
  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
  button?: (props?: React.PropsWithChildren) => React.ReactNode;
  defaultValue?: any;
} & InputProps &
  FormInputProps &
  Partial<FormLabelProps> &
  FormInputWithIconProps &
  UseControllerProps<TFieldValues, TName>;

export const FormQuantityField = forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement> & FormQuantityFieldProps
>((props, ref) => {
  const {
    name,
    label,
    hint,
    placeholder,
    description,

    required,
    className,
    leadIcon,
    trailIcon,
    button,
    defaultValue,
  } = props;

  const { control } = useFormContext();

  const [precision, setPrecision] = useState<number>(Quantity.DEFAULT_PRECISION);

  const transform = {
    input: (value: QuantityObject) => {
      const quantityOrError = Quantity.create(value);

      if (quantityOrError.isFailure) {
        throw quantityOrError.error;
      }

      const quantityValue = quantityOrError.object;
      setPrecision(quantityValue.getPrecision());
      return quantityValue.toString();
    },
    output: (event: React.ChangeEvent<HTMLInputElement>): QuantityObject => {
      const value = parseFloat(event.target.value);
      const output = !isNaN(value) ? value : 0;

      const quantityOrError = Quantity.create({
        amount: output * Math.pow(10, precision),
        precision,
      });

      if (quantityOrError.isFailure) {
        throw quantityOrError.error;
      }

      return quantityOrError.object.toObject();
    },
  };

  return (
    <Controller
      defaultValue={defaultValue}
      control={control}
      name={name}
      rules={{
        required,
      }}
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      render={({ field, fieldState, formState }) => {
        return (
          <input
            type='number'
            {...field}
            className='text-right'
            placeholder={placeholder}
            onChange={(e) => field.onChange(transform.output(e))}
            value={transform.input(field.value)}
          />
        );

        return (
          <FormItem ref={ref} className={cn(className, "space-y-3")}>
            {label && <FormLabel label={label} hint={hint} required={required} />}
            <div className={cn(button ? "flex" : null)}>
              <div
                className={cn(
                  leadIcon ? "relative flex items-stretch flex-grow focus-within:z-10" : ""
                )}
              >
                {leadIcon && (
                  <div className='absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none'>
                    {createElement(
                      leadIcon,
                      {
                        className: "h-5 w-5 text-muted-foreground",
                        "aria-hidden": true,
                      },
                      null
                    )}
                  </div>
                )}

                <FormControl
                  className={cn("block", leadIcon ? "pl-10" : "", trailIcon ? "pr-10" : "")}
                >
                  <input
                    type='number'
                    placeholder={placeholder}
                    className={cn(
                      fieldState.error ? "border-destructive focus-visible:ring-destructive" : ""
                    )}
                    {...field}
                    onInput={(e) => field.onChange(transform.output(e))}
                    value={transform.input(field.value)}
                  />
                </FormControl>

                {trailIcon && (
                  <div className='absolute inset-y-0 right-0 flex items-center pl-3 pointer-events-none'>
                    {createElement(
                      trailIcon,
                      {
                        className: "h-5 w-5 text-muted-foreground",
                        "aria-hidden": true,
                      },
                      null
                    )}
                  </div>
                )}
              </div>
              {button && <>{createElement(button)}</>}
            </div>

            {description && <FormDescription>{description}</FormDescription>}
            <FormErrorMessage />
          </FormItem>
        );
      }}
    />
  );
});
