'use client';

import { useEffect, useState } from 'react';
import { Button, Input, LoaderSpinner, Select } from '@theme/components';
import { twMerge } from 'tailwind-merge';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import DynamicForm from './dynamic-form';

import {
  AllFieldClassesType,
  FieldPropertiesType,
  FormField,
  FormPropertiesType,
  Schema
} from '@akinon/next/types';
import { useLocalization } from '@akinon/next/hooks';

export function GenerateFormFields({
  schema,
  allFieldClasses,
  fieldProperties,
  formProperties,
  submitButtonText
}: {
  schema: Schema;
  allFieldClasses?: AllFieldClassesType;
  fieldProperties: FieldPropertiesType[];
  formProperties: FormPropertiesType;
  submitButtonText: string;
}) {
  const { t } = useLocalization();
  const [fields, setFields] = useState([]);
  const [loading, setIsLoading] = useState(true);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [submitStatus, setSubmitStatus] = useState<
    'idle' | 'success' | 'error'
  >('idle');
  const [submitMessage, setSubmitMessage] = useState('');

  const generateValidationSchema = () => {
    const schemaObject = {};

    Object.keys(schema).forEach((key) => {
      const item = schema[key];

      if (item.validators) {
        const { validators } = item;
        let fieldSchema;

        if (item.input_type === 'integer') {
          fieldSchema = yup
            .number()
            .transform((value) => (Number.isNaN(value) ? null : value))
            .nullable();
        } else {
          fieldSchema = yup.string();
        }

        if (validators.required) {
          fieldSchema = fieldSchema.required();
        }

        if (validators.min_length !== undefined) {
          fieldSchema = fieldSchema.min(validators.min_length);
        }

        if (validators.max_length !== undefined) {
          fieldSchema = fieldSchema.max(validators.max_length);
        }

        if (validators.min_value !== undefined) {
          fieldSchema = fieldSchema.min(validators.min_value);
        }

        if (validators.max_value !== undefined) {
          fieldSchema = fieldSchema.max(validators.max_value);
        }

        schemaObject[item.key] = fieldSchema;
      }
    });

    return yup.object().shape(schemaObject);
  };

  const {
    handleSubmit,
    register,
    reset,
    formState: { errors }
  } = useForm<FormField>({
    resolver: yupResolver(generateValidationSchema())
  });

  useEffect(() => {
    const sortedFields = Object.keys(schema)
      .map((key) => {
        const item = schema[key];

        fieldProperties.find((fieldProperty) => {
          if (fieldProperty.key === item.key) {
            item.class = fieldProperty.className;
            item.attributes = fieldProperty.attributes;
            item.labelClass = fieldProperty.labelClassName;
            item.wrapperClass = fieldProperty.wrapperClassName;
          }
        });

        return item;
      })
      .sort((a, b) => a.order - b.order);

    setFields(sortedFields);
    setIsLoading(false);
  }, [schema, fieldProperties]);

  const onSubmit = async (data) => {
    setIsSubmitting(true);
    setSubmitStatus('idle');
    setSubmitMessage('');

    try {
      const formData = new FormData();

      Object.keys(data).forEach((key) => {
        formData.append(key, data[key]);
      });

      const response = await fetch(formProperties.actionUrl, {
        method: 'POST',
        body: formData
      });

      if (response.ok) {
        setSubmitStatus('success');
        setSubmitMessage(t('common.forms.success'));
        reset();
      } else {
        setSubmitStatus('error');
        setSubmitMessage(t('common.forms.error'));
      }
    } catch (error) {
      console.error(t('common.forms.submit_error'), error);
      setSubmitStatus('error');
      setSubmitMessage(t('common.forms.error'));
    } finally {
      setIsSubmitting(false);
    }
  };

  const generateField = (field: FormField) => {
    const inputType = field.input_type;

    if (!inputType) {
      return null;
    }

    switch (inputType) {
      case 'text':
      case 'email':
        return (
          <div
            className={twMerge(
              allFieldClasses?.wrapperClassName,
              field.wrapperClass
            )}
            key={field.id}
          >
            <label
              htmlFor={field.id}
              className={twMerge(
                allFieldClasses?.labelClassName,
                field.labelClass
              )}
            >
              {field.label}
            </label>
            <Input
              id={field.id}
              type={inputType === 'text' ? 'text' : 'email'}
              className={twMerge(allFieldClasses?.className, field.class)}
              name={field.key}
              {...field.attributes}
              error={errors[field.key] as any}
              {...register(field.key)}
            />
          </div>
        );

      case 'integer':
        return (
          <div
            className={twMerge(
              allFieldClasses?.wrapperClassName,
              field.wrapperClass
            )}
            key={field.id}
          >
            <label
              htmlFor={field.id}
              className={twMerge(
                allFieldClasses?.labelClassName,
                field.labelClass
              )}
            >
              {field.label}
            </label>
            <Input
              id={field.id}
              type="number"
              className={twMerge(allFieldClasses?.className, field?.class)}
              name={field.key}
              {...field.attributes}
              error={errors[field.key] as any}
              {...register(field.key, { valueAsNumber: true })}
            />
          </div>
        );

      case 'text_area':
        return (
          <div
            className={twMerge(
              allFieldClasses?.wrapperClassName,
              field.wrapperClass
            )}
            key={field.id}
          >
            <label
              htmlFor={field.id}
              className={twMerge(
                allFieldClasses?.labelClassName,
                field.labelClass
              )}
            >
              {field.label}
            </label>
            <textarea
              id={field.id}
              className={twMerge(allFieldClasses?.className, field.class)}
              name={field.key}
              {...field.attributes}
              {...register(field.key)}
            />
            {errors[field.key] && (
              <span className="mt-1 text-sm text-error">
                {String(errors[field.key].message)}
              </span>
            )}
          </div>
        );

      case 'date':
      case 'datetime':
        return (
          <div
            className={twMerge(
              allFieldClasses?.wrapperClassName,
              field.wrapperClass
            )}
            key={field.id}
          >
            <label
              htmlFor={field.id}
              className={twMerge(
                allFieldClasses?.labelClassName,
                field.labelClass
              )}
            >
              {field.label}
            </label>
            <Input
              id={field.id}
              type={inputType === 'date' ? 'date' : 'datetime-local'}
              className={twMerge(allFieldClasses?.className, field?.class)}
              name={field.key}
              {...field.attributes}
              error={errors[field.key] as any}
              {...register(field.key)}
            />
          </div>
        );

      case 'integer_range':
        return (
          <div
            className={twMerge(
              allFieldClasses?.wrapperClassName,
              field.wrapperClass
            )}
            key={field.id}
          >
            <label
              htmlFor={field.id}
              className={twMerge(
                allFieldClasses?.labelClassName,
                field.labelClass
              )}
            >
              {field.label}
            </label>
            <Select
              id={field.id}
              className={twMerge(allFieldClasses?.className, field?.class)}
              name={field.key}
              {...field.attributes}
              options={field.choices.map((choice) => ({
                value: choice,
                label: choice
              }))}
              {...field.attributes}
              error={errors[field.key] as any}
              {...register(field.key)}
            />
          </div>
        );

      case 'file':
      case 'image':
        return (
          <div
            className={twMerge(
              allFieldClasses?.wrapperClassName,
              field.wrapperClass
            )}
            key={field.id}
          >
            <label
              htmlFor={field.id}
              className={twMerge(
                allFieldClasses?.labelClassName,
                field.labelClass
              )}
            >
              {field.label}
            </label>
            <Input
              id={field.id}
              type="file"
              className={twMerge(allFieldClasses?.className, field?.class)}
              name={field.key}
              {...field.attributes}
              error={errors[field.key] as any}
              {...register(field.key)}
            />
          </div>
        );

      default:
        return null;
    }
  };

  return (
    <div>
      <DynamicForm
        className={formProperties.className}
        onSubmit={handleSubmit(onSubmit)}
      >
        {loading ? (
          <LoaderSpinner />
        ) : (
          <>
            {submitStatus === 'success' && (
              <div className="mb-4 p-3 bg-green-100 border border-green-400 text-green-700 rounded">
                {submitMessage}
              </div>
            )}

            {submitStatus === 'error' && (
              <div className="mb-4 p-3 bg-red-100 border border-red-400 text-red-700 rounded">
                {submitMessage}
              </div>
            )}

            {fields.map((field: FormField) => generateField(field))}

            <Button type="submit" className="w-full" disabled={isSubmitting}>
              {isSubmitting ? t('common.forms.sending') : submitButtonText}
            </Button>
          </>
        )}
      </DynamicForm>
    </div>
  );
}
