'use client';

import { useCallback, useState } from 'react';
import { useRouter } from 'next/navigation';
import { useFormik } from 'formik';
import { toast } from 'react-hot-toast';

import { Icon } from '@iconify/react';
import Link from 'next/link';
import Button from '@/components/Button';
import { useModal } from '@/components/Modal';
import ConfirmationModal from '@/components/modal/ConfirmationModal';
import SelectInput, {
  OptionType,
  useSelect,
} from '@/components/input/SelectInput';
import DateInput from '@/components/input/DateInput';
import ExpenseKandangsTable from '@/components/pages/expense/form/ExpenseKandangsTable';
import DropFileInput from '@/components/input/DropFileInput';
import ExpenseRequestKandangDetailExpense from '@/components/pages/expense/form/ExpenseRequestKandangDetailExpense';
import RequirePermission from '@/components/helper/RequirePermission';

import {
  ExpenseRequestFormSchema,
  ExpenseRequestFormValues,
  getExpenseFormInitialValues,
  UpdateExpenseRequestFormSchema,
} from '@/components/pages/expense/form/ExpenseRequestForm.schema';
import { isResponseError } from '@/lib/api-helper';
import {
  Expense,
  CreateExpensePayload,
  UpdateExpensePayload,
} from '@/types/api/expense';
import { ExpenseApi } from '@/services/api/expense';
import { cn, sleep } from '@/lib/helper';
import { LocationApi, SupplierApi } from '@/services/api/master-data';
import { ACCEPTED_FILE_TYPE } from '@/config/constant';
import { Supplier } from '@/types/api/master-data/supplier';
import { getUniqueFormikErrors } from '@/lib/formik-helper';
import AlertErrorList from '@/components/helper/form/FormErrors';

interface ExpenseFormProps {
  type?: 'add' | 'edit' | 'detail';
  initialValues?: Expense;
}

const ExpenseRequestForm = ({
  type = 'add',
  initialValues,
}: ExpenseFormProps) => {
  const router = useRouter();

  // Modal hooks
  const deleteModal = useModal();
  const approveModal = useModal();
  const rejectModal = useModal();

  const [expenseFormErrorMessage, setExpenseFormErrorMessage] = useState('');
  const [formErrorList, setFormErrorList] = useState<string[]>([]);

  const createExpenseHandler = useCallback(
    async (payload: CreateExpensePayload) => {
      const createExpenseRes = await ExpenseApi.create(
        ExpenseApi.convertExpenseRequestPayloadToFormData(payload)
      );

      if (isResponseError(createExpenseRes)) {
        setExpenseFormErrorMessage(createExpenseRes.message);
        return;
      }

      toast.success(createExpenseRes?.message as string);
      router.push('/expense');
    },
    [router]
  );

  const updateExpenseHandler = useCallback(
    async (
      expenseId: number,
      payload: UpdateExpensePayload,
      deletedDocumentIds: number[]
    ) => {
      const updateExpenseRes = await ExpenseApi.update(
        expenseId,
        ExpenseApi.convertExpenseRequestUpdatePayloadToFormData(payload),
        deletedDocumentIds
      );

      if (updateExpenseRes?.status === 'error') {
        setExpenseFormErrorMessage(updateExpenseRes.message);
        return;
      }

      toast.success(updateExpenseRes?.message as string);
      router.refresh();
      router.push('/expense');
    },
    [router]
  );

  const formik = useFormik<ExpenseRequestFormValues>({
    initialValues: getExpenseFormInitialValues(initialValues),
    enableReinitialize: true,
    validationSchema:
      type === 'edit'
        ? UpdateExpenseRequestFormSchema
        : ExpenseRequestFormSchema,
    onSubmit: async (values) => {
      setExpenseFormErrorMessage('');

      const expensePayload: CreateExpensePayload = {
        category: formik.values.category?.value as 'BOP' | 'NON-BOP',
        location_id: values.location_id as number,
        transaction_date: values?.transaction_date as string,
        supplier_id: values.supplier?.value as number,
        documents: values.documents as File[],
        expense_nonstocks: values.expense_nonstocks.map((expenseNonstock) => {
          const basePayload = {
            cost_items: expenseNonstock.cost_items.map((costItem) => ({
              nonstock_id: costItem.nonstock?.value as number,
              quantity: parseFloat(String(costItem.quantity)) as number,
              price: parseFloat(String(costItem.price)) as number,
              notes: costItem.notes ?? '',
            })),
          };

          return expenseNonstock.kandang_id
            ? { ...basePayload, kandang_id: expenseNonstock.kandang_id }
            : basePayload;
        }),
      };

      switch (type) {
        case 'add':
          await createExpenseHandler(expensePayload);
          break;

        case 'edit':
          const expenseUpdatePayload: UpdateExpensePayload = {
            category: formik.values.category?.value as 'BOP' | 'NON-BOP',
            location_id: values.location_id as number,
            transaction_date: values?.transaction_date as string,
            supplier_id: values.supplier?.value as number,
            documents: values.documents as File[],
            expense_nonstocks: values.expense_nonstocks.map(
              (expenseNonstock) => {
                const basePayload = {
                  cost_items: expenseNonstock.cost_items.map((costItem) => ({
                    nonstock_id: costItem.nonstock?.value as number,
                    quantity: parseFloat(String(costItem.quantity)) as number,
                    price: parseFloat(String(costItem.price)) as number,
                    notes: costItem.notes ?? '',
                  })),
                };

                return expenseNonstock.kandang_id
                  ? { ...basePayload, kandang_id: expenseNonstock.kandang_id }
                  : basePayload;
              }
            ),
          };

          await updateExpenseHandler(
            initialValues?.id as number,
            expenseUpdatePayload,
            formik.values.deleted_documents ?? []
          );
          break;
      }
    },
  });

  const { setFieldValue, setFieldTouched } = formik;

  const {
    setInputValue: setLocationInputValue,
    options: locationOptions,
    isLoadingOptions: isLoadingLocationOptions,
    loadMore: loadMoreLocations,
  } = useSelect<Location>(LocationApi.basePath, 'id', 'name');

  const {
    setInputValue: setVendorInputValue,
    options: supplierOptions,
    isLoadingOptions: isLoadingVendorOptions,
    loadMore: loadMoreSuppliers,
  } = useSelect<Supplier>(SupplierApi.basePath, 'id', 'name');

  const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
    setFieldTouched('category', true);
    setFieldValue('category', val);
  };

  const locationChangeHandler = useCallback(
    (val: OptionType | OptionType[] | null) => {
      const location = val as OptionType | null;
      const locationId = location ? Number(location.value) : 0;

      setFieldTouched('location', true);
      setFieldValue('location', location);
      setFieldTouched('location_id', true);
      setFieldValue('location_id', locationId);
    },
    [setFieldTouched, setFieldValue]
  );

  const kandangsChangeHandler = (
    kandangs: { id?: number; name?: string }[]
  ) => {
    formik.setFieldTouched('kandangs', true);
    formik.setFieldValue('kandangs', kandangs);

    // If no kandangs selected, create expense item for location
    if (kandangs.length === 0) {
      formik.setFieldValue('expense_nonstocks', [
        {
          cost_items: [
            {
              nonstock: null,
              nonstock_id: 0,
              quantity: undefined,
              price: undefined,
              notes: '',
            },
          ],
        },
      ]);
      return;
    }

    const newExpenseNonstocks: typeof formik.values.expense_nonstocks = [];

    kandangs.forEach((kandangItem) => {
      if (!kandangItem.id) return;

      const existingExpenseNonstock = formik.values.expense_nonstocks?.find(
        (expenseNonstockItem) =>
          expenseNonstockItem.kandang_id === kandangItem.id
      );

      newExpenseNonstocks.push({
        kandang_id: kandangItem.id,
        cost_items: existingExpenseNonstock?.cost_items || [
          {
            nonstock: null,
            nonstock_id: 0,
            quantity: undefined,
            price: undefined,
            notes: '',
          },
        ],
      });
    });

    formik.setFieldValue('expense_nonstocks', newExpenseNonstocks);
  };

  const supplierChangeHandler = (val: OptionType | OptionType[] | null) => {
    formik.setFieldTouched('supplier', true);
    formik.setFieldTouched('supplier_id', true);
    formik.setFieldValue('supplier', val);

    const supplierId = Array.isArray(val) ? val[0]?.value : val?.value;
    formik.setFieldValue('supplier_id', supplierId ?? 0);
  };

  const requestDocumentsChangeHandler = (val: File[]) => {
    formik.setFieldTouched('documents', true);

    const invalidFiles = val.filter((file) => file.size > 5 * 1024 * 1024);
    if (invalidFiles.length > 0) {
      toast.error('Ukuran dokumen maksimal 5 MB!');
      return;
    }

    formik.setFieldValue('documents', val);
  };

  const requestDocumentsDeleteHandler = (deletedFileIdx: number) => {
    const newRequestDocuments = formik.values.documents;

    newRequestDocuments?.splice(deletedFileIdx, 1);

    formik.setFieldValue('documents', newRequestDocuments);
  };

  const deleteDocumentClickHandler = (
    deletedDocumentIdx: number,
    deletedDocumentId: number
  ) => {
    const newDeletedDocumentIds = [...(formik.values.deleted_documents ?? [])];
    const newExistingDocuments = [
      ...(formik.values.existing_documents ?? []),
    ].filter((_, idx) => idx !== deletedDocumentIdx);

    newDeletedDocumentIds.push(deletedDocumentId);

    formik.setFieldTouched('deleted_documents', true);
    formik.setFieldValue('deleted_documents', newDeletedDocumentIds);

    formik.setFieldTouched('existing_documents', true);
    formik.setFieldValue('existing_documents', newExistingDocuments);
  };

  const deleteExpenseClickHandler = () => {
    deleteModal.openModal();
  };

  const confirmationModalRejectClickHandler = async () => {
    await sleep(750);

    rejectModal.closeModal();
    toast.success('Berhasil melakukan reject biaya operasional!');
  };

  const confirmationModalApproveClickHandler = async () => {
    await sleep(750);

    approveModal.closeModal();
    toast.success('Berhasil melakukan approve biaya operasional!');
  };

  const confirmationModalDeleteClickHandler = async () => {
    await ExpenseApi.delete(initialValues?.id as number);

    deleteModal.closeModal();
    toast.success('Successfully delete Expense!');
    router.push('/expense');
  };

  const handleValidateForm = async () => {
    const errors = await formik.validateForm();

    if (Object.keys(errors).length > 0) {
      const errorMessages = getUniqueFormikErrors(errors);
      setFormErrorList(errorMessages);
      return;
    }
  };

  const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    handleValidateForm();
    formik.handleSubmit(e);
  };

  return (
    <>
      <section className='w-full'>
        <header className='flex flex-col gap-4'>
          <Button
            href='/expense'
            variant='link'
            className='w-fit p-0 text-primary'
          >
            <Icon icon='uil:arrow-left' width={24} height={24} />
            Kembali
          </Button>

          <h1 className='text-2xl font-bold text-center'>
            {type === 'add' && 'Tambah Biaya Operasional'}
            {type === 'edit' && 'Edit Biaya Operasional'}
            {type === 'detail' && 'Detail Biaya Operasional'}
          </h1>
        </header>

        <form
          onSubmit={handleFormSubmit}
          onReset={formik.handleReset}
          className='w-full mt-8 flex flex-col gap-6'
        >
          <div className='grid grid-cols-12 gap-4'>
            <SelectInput
              label='Kategori'
              required
              placeholder='Pilih Kategori'
              value={formik.values.category}
              onChange={categoryChangeHandler}
              isError={
                formik.touched.category && Boolean(formik.errors.category)
              }
              errorMessage={
                formik.touched.category && formik.errors.category
                  ? typeof formik.errors.category === 'object'
                    ? 'Kategori wajib diisi!'
                    : (formik.errors.category as string)
                  : undefined
              }
              options={[
                {
                  value: 'BOP',
                  label: 'BOP',
                },
                {
                  value: 'NON-BOP',
                  label: 'NON-BOP',
                },
              ]}
              className={{ wrapper: 'col-span-12 sm:col-span-4' }}
            />

            <SelectInput
              label='Lokasi'
              required
              placeholder='Pilih Lokasi'
              value={formik.values.location}
              onChange={locationChangeHandler}
              options={locationOptions}
              onInputChange={setLocationInputValue}
              isLoading={isLoadingLocationOptions}
              onMenuScrollToBottom={loadMoreLocations}
              isError={
                formik.touched.location_id && Boolean(formik.errors.location_id)
              }
              errorMessage={formik.errors.location_id as string}
              isClearable
              className={{ wrapper: 'col-span-12 sm:col-span-4' }}
            />

            <DateInput
              name='transaction_date'
              label='Tanggal Transaksi'
              required
              value={formik.values.transaction_date}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              isError={
                formik.touched.transaction_date &&
                Boolean(formik.errors.transaction_date)
              }
              errorMessage={formik.errors.transaction_date as string}
              className={{
                wrapper: 'col-span-12 sm:col-span-4',
              }}
            />

            <ExpenseKandangsTable
              type={type}
              locationId={formik.values.location?.value}
              selectedKandangs={formik.values.kandangs ?? []}
              onChange={kandangsChangeHandler}
              className={{
                wrapper: 'w-full col-span-12',
              }}
            />

            <SelectInput
              label='Vendor'
              required
              placeholder='Pilih Vendor'
              value={formik.values.supplier}
              onChange={supplierChangeHandler}
              options={supplierOptions}
              onInputChange={setVendorInputValue}
              isLoading={isLoadingVendorOptions}
              onMenuScrollToBottom={loadMoreSuppliers}
              isError={
                formik.touched.supplier_id && Boolean(formik.errors.supplier_id)
              }
              errorMessage={formik.errors.supplier_id as string}
              className={{ wrapper: 'col-span-12' }}
            />

            <RequirePermission permissions='lti.expense.document'>
              <DropFileInput
                label='Dokumen Pengajuan'
                name='documents'
                values={formik.values.documents}
                onChange={requestDocumentsChangeHandler}
                onDelete={requestDocumentsDeleteHandler}
                accept={{
                  ...ACCEPTED_FILE_TYPE.PDF,
                  ...ACCEPTED_FILE_TYPE.IMAGE,
                }}
                className={{
                  wrapper: 'col-span-12',
                  inputWrapper: 'h-12 flex items-center',
                }}
              />
            </RequirePermission>

            {formik.values.existing_documents &&
              formik.values.existing_documents.length > 0 && (
                <div className='w-full col-span-12'>
                  <ul className='pl-4 list-disc'>
                    {formik.values.existing_documents.map(
                      (existingDocument, existingDocumentIdx) => (
                        <li key={existingDocumentIdx}>
                          <div className='w-full flex flex-wrap justify-between'>
                            <Link
                              href={existingDocument.url}
                              target='_blank'
                              rel='noopener noreferrer'
                              className='text-blue-500 underline'
                            >
                              {existingDocument.name}{' '}
                              <Icon
                                icon='cuida:open-in-new-tab-outline'
                                width={12}
                                height={12}
                                className='inline'
                              />
                            </Link>

                            <Button
                              type='button'
                              variant='ghost'
                              color='error'
                              onClick={() => {
                                deleteDocumentClickHandler(
                                  existingDocumentIdx,
                                  existingDocument.id
                                );
                              }}
                              className='p-1 rounded-full text-error focus-visible:text-error-content hover:text-error-content'
                            >
                              <Icon
                                icon='fluent:delete-12-regular'
                                width={20}
                                height={20}
                              />
                            </Button>
                          </div>
                        </li>
                      )
                    )}
                  </ul>
                </div>
              )}

            <ExpenseRequestKandangDetailExpense
              type={type}
              formik={formik}
              supplierId={formik.values.supplier?.value as number}
              location={formik.values.location}
              className={{
                wrapper: 'col-span-12',
              }}
            />
          </div>

          {expenseFormErrorMessage && (
            <div role='alert' className='alert alert-error'>
              <Icon
                icon='material-symbols:error-outline'
                width={24}
                height={24}
              />
              <span>{expenseFormErrorMessage}</span>
            </div>
          )}

          {formErrorList.length > 0 && (
            <AlertErrorList
              formErrorList={formErrorList}
              onClose={() => setFormErrorList([])}
            />
          )}

          <div className='flex flex-row justify-between gap-2 flex-wrap'>
            {type !== 'add' && (
              <div className='flex flex-row justify-start gap-2'>
                <RequirePermission permissions='lti.expense.delete'>
                  <Button
                    type='button'
                    color='error'
                    onClick={deleteExpenseClickHandler}
                    className='px-4'
                  >
                    <Icon
                      icon='material-symbols:delete-outline-rounded'
                      width={24}
                      height={24}
                      className='justify-start text-sm'
                    />
                    Delete
                  </Button>
                </RequirePermission>

                {type !== 'edit' && (
                  <RequirePermission permissions='lti.expense.update'>
                    <Button
                      type='button'
                      color='warning'
                      href={`/expense/detail/edit/?expenseId=${initialValues?.id}`}
                      className='px-4'
                    >
                      <Icon
                        icon='material-symbols:edit-outline'
                        width={24}
                        height={24}
                        className='justify-start text-sm'
                      />
                      Edit
                    </Button>
                  </RequirePermission>
                )}
              </div>
            )}

            {type !== 'detail' && (
              <div
                className={cn('flex flex-row justify-end gap-2', {
                  'w-full': type === 'add',
                })}
              >
                <Button type='reset' color='warning' className='px-4'>
                  Reset
                </Button>

                <Button
                  type='submit'
                  color='primary'
                  isLoading={formik.isSubmitting}
                  disabled={formik.isSubmitting}
                  className='px-4'
                >
                  Submit
                </Button>
              </div>
            )}
          </div>
        </form>
      </section>

      {type !== 'add' && (
        <ConfirmationModal
          ref={deleteModal.ref}
          type='error'
          text='Apakah anda yakin ingin menghapus data Expense ini?'
          secondaryButton={{
            text: 'Tidak',
          }}
          primaryButton={{
            text: 'Ya',
            color: 'error',
            onClick: confirmationModalDeleteClickHandler,
          }}
        />
      )}

      {type === 'detail' && (
        <>
          <ConfirmationModal
            ref={approveModal.ref}
            type='success'
            text='Apakah anda yakin ingin approve data transfer ke laying ini?'
            secondaryButton={{
              text: 'Tidak',
            }}
            primaryButton={{
              text: 'Ya',
              color: 'success',
              onClick: confirmationModalApproveClickHandler,
            }}
          />

          <ConfirmationModal
            ref={rejectModal.ref}
            type='error'
            text='Apakah anda yakin ingin reject data transfer ke laying ini?'
            secondaryButton={{
              text: 'Tidak',
            }}
            primaryButton={{
              text: 'Ya',
              color: 'error',
              onClick: confirmationModalRejectClickHandler,
            }}
          />
        </>
      )}
    </>
  );
};

export default ExpenseRequestForm;
