"use client";

import InputGroup from "@/components/FormElements/InputGroup";
import { ShowcaseSection } from "@/components/Layouts/showcase-section";
import { cn } from "@/lib/utils";
import type {
  Control,
  FieldErrors,
  FieldValues,
  Path,
  RegisterOptions,
  SubmitHandler,
  UseFormHandleSubmit,
  UseFormRegister,
} from "react-hook-form";
import { get } from "react-hook-form";
import type {
  HTMLInputAutoCompleteAttribute,
  HTMLInputTypeAttribute,
  ReactNode,
} from "react";
import { memo } from "react";

export interface DynamicFieldContext<TValues extends FieldValues> {
  control: Control<TValues>;
  errors: FieldErrors<TValues>;
  register: UseFormRegister<TValues>;
  error?: string;
}

interface DynamicFieldBase<TValues extends FieldValues> {
  name: Path<TValues>;
  className?: string;
  hidden?: boolean;
}

export interface DynamicInputField<
  TValues extends FieldValues,
> extends DynamicFieldBase<TValues> {
  kind: "input";
  label: string;
  type: HTMLInputTypeAttribute;
  placeholder?: string;
  required?: boolean;
  disabled?: boolean;
  autoComplete?: HTMLInputAutoCompleteAttribute;
  dataCy?: string;
  icon?: ReactNode;
  iconPosition?: "left" | "right";
  rules?: RegisterOptions<TValues, Path<TValues>>;
}

export interface DynamicCustomField<
  TValues extends FieldValues,
> extends DynamicFieldBase<TValues> {
  kind: "custom";
  render: (context: DynamicFieldContext<TValues>) => ReactNode;
}

export type DynamicFormField<TValues extends FieldValues> =
  | DynamicInputField<TValues>
  | DynamicCustomField<TValues>;

export interface DynamicFormSection<TValues extends FieldValues> {
  id: string;
  title?: ReactNode;
  className?: string;
  rootClassName?: string;
  fields: DynamicFormField<TValues>[];
}

interface DynamicFormBuilderProps<TValues extends FieldValues> {
  sections: DynamicFormSection<TValues>[];
  register: UseFormRegister<TValues>;
  control: Control<TValues>;
  errors: FieldErrors<TValues>;
  handleSubmit: UseFormHandleSubmit<TValues>;
  onSubmit: SubmitHandler<TValues>;
  dataCy?: string;
  className?: string;
  header?: ReactNode;
  sectionsClassName?: string;
  footer?: ReactNode;
  footerPlacement?: "last-section" | "form";
}

const getErrorMessage = <TValues extends FieldValues>(
  errors: FieldErrors<TValues>,
  name: Path<TValues>,
) => {
  const message = get(errors, name)?.message;
  return typeof message === "string" ? message : undefined;
};

interface DynamicSectionProps<TValues extends FieldValues> {
  section: DynamicFormSection<TValues>;
  register: UseFormRegister<TValues>;
  control: Control<TValues>;
  errors: FieldErrors<TValues>;
  footer?: ReactNode;
}

const DynamicSectionComponent = <TValues extends FieldValues>({
  section,
  register,
  control,
  errors,
  footer,
}: DynamicSectionProps<TValues>) => (
  <ShowcaseSection
    title={section.title}
    rootClassName={section.rootClassName}
    className={cn("grid grid-cols-1 gap-3 md:grid-cols-2", section.className)}
  >
    {section.fields.map((field) => {
      if (field.hidden) return null;

      if (field.kind === "custom") {
        return (
          <div key={field.name} className={field.className}>
            {field.render({
              control,
              errors,
              register,
              error: getErrorMessage(errors, field.name),
            })}
          </div>
        );
      }

      return (
        <InputGroup<TValues>
          key={field.name}
          {...register(field.name, field.rules)}
          name={field.name}
          label={field.label}
          type={field.type}
          placeholder={field.placeholder}
          required={field.required}
          disabled={field.disabled}
          autoComplete={field.autoComplete}
          dataCy={field.dataCy}
          icon={field.icon}
          iconPosition={field.iconPosition}
          className={field.className}
          errors={errors}
        />
      );
    })}

    {footer ? <div className="md:col-span-2">{footer}</div> : null}
  </ShowcaseSection>
);

const DynamicSection = memo(
  DynamicSectionComponent,
) as typeof DynamicSectionComponent;

const DynamicFormBuilder = <TValues extends FieldValues>({
  sections,
  register,
  control,
  errors,
  handleSubmit,
  onSubmit,
  dataCy,
  className,
  header,
  sectionsClassName,
  footer,
  footerPlacement = "last-section",
}: DynamicFormBuilderProps<TValues>) => (
  <form
    className={cn("grid grid-cols-1 gap-9", className)}
    onSubmit={handleSubmit(onSubmit)}
    data-cy={dataCy}
    noValidate
  >
    {header}
    <div className={sectionsClassName}>
      {sections.map((section, sectionIndex) => (
        <DynamicSection
          key={section.id}
          section={section}
          register={register}
          control={control}
          errors={errors}
          footer={
            footerPlacement === "last-section" &&
            sectionIndex === sections.length - 1
              ? footer
              : undefined
          }
        />
      ))}
    </div>
    {footerPlacement === "form" ? footer : null}
  </form>
);

export default DynamicFormBuilder;
