import { useEffect, useMemo } from 'react';
import * as Yup from 'yup';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
// @mui
import LoadingButton from '@mui/lab/LoadingButton';
import Card from '@mui/material/Card';
import Stack from '@mui/material/Stack';
// routes
import { paths } from 'src/routes/paths';
import { useRouter } from 'src/routes/hooks';
// types
import { Invoice } from 'mvpmasters-shared';
// hooks
import { useBoolean } from 'src/hooks/use-boolean';
// components
import FormProvider from 'src/components/hook-form';
//
import { incrementInvoiceNumber } from 'src/utils/increment-invoice-number';
import uploadToFirebaseStorage from 'src/utils/upload-to-firebase-storage';
import { pdf } from '@react-pdf/renderer';
import { useGetSettings } from 'src/api/settings';
import {
  collections,
  documents,
  firestoreBatch,
  generateId,
  updateDocument,
} from 'src/lib/firestore';
import { useGetServices } from 'src/api/service';
import { mutate } from 'swr';
import { Timestamp } from 'firebase/firestore';
import InvoiceNewEditStatusDate from './invoice-new-edit-status-date';
import InvoiceNewEditAddress from './invoice-new-edit-address';
import InvoiceNewEditDetails from './invoice-new-edit-details';
import InvoicePDF from './invoice-pdf';

// ----------------------------------------------------------------------

type Props = {
  isCopy?: boolean;
  currentInvoice?: Invoice;
};

export const monthNames = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'July',
  'August',
  'September',
  'October',
  'November',
  'December',
];

export default function InvoiceNewEditForm({ isCopy, currentInvoice }: Props) {
  const router = useRouter();

  const loadingSave = useBoolean();

  const loadingSend = useBoolean();

  const itemSchema = Yup.object({
    title: Yup.string().required('Title is required'),
    description: Yup.string().required('Description is required'),
    service: Yup.string().nullable(),
    quantity: Yup.number().required('Quantity is required').min(0.5, 'Quantity must be at least 1'),
    price: Yup.number().required('Price is required').min(0, 'Price must be at least 0'),
    total: Yup.number().required('Total is required').min(0, 'Total must be at least 0'),
  });

  const NewInvoiceSchema = Yup.object().shape({
    invoiceNumber: Yup.string().nullable().required('Invoice number is required'),
    createDate: Yup.mixed<any>().nullable().required('Create date is required'),
    dueDate: Yup.mixed<any>()
      .required('Due date is required')
      .test(
        'date-min',
        'Due date must be later than create date',
        (value, { parent }) => value.getTime() > parent.createDate.getTime()
      ),
    invoiceFrom: Yup.mixed<any>().nullable().required('Invoice from is required'),
    invoiceTo: Yup.mixed<any>().nullable().required('Invoice to is required'),
    currency: Yup.string().oneOf(['EUR', 'USD']).required('Currency is required'),
    quantityType: Yup.string()
      .oneOf(['Unit', 'Hour', 'Sprint', 'Month'])
      .required('Quantity type is required'),
    month: Yup.string().oneOf(monthNames).required('Month is required'),
    status: Yup.string().required(),
    totalAmount: Yup.number().required(),
    // not required
    taxes: Yup.number(),
    discount: Yup.number(),
    items: Yup.array()
      .of(itemSchema)
      .required('At least one item is required')
      .min(1, 'At least one item must be provided'),
  });

  const { services: invoiceServices } = useGetServices();
  const { settings, settingsEmpty, settingsLoading } = useGetSettings();

  const defaultValues = useMemo(
    () => ({
      invoiceNumber:
        !isCopy && currentInvoice?.invoiceNumber
          ? currentInvoice?.invoiceNumber
          : incrementInvoiceNumber(settings?.invoice?.lastInvoiceNumber),
      createDate: currentInvoice?.createDate?.toDate() || new Date(),
      dueDate: currentInvoice?.dueDate?.toDate() || null,
      invoiceFrom: currentInvoice?.invoiceFrom || null,
      invoiceTo: currentInvoice?.invoiceTo || null,
      currency: currentInvoice?.currency || 'EUR',
      quantityType: currentInvoice?.quantityType || 'Unit',
      month: currentInvoice?.month || monthNames[new Date().getMonth()],
      // not required
      taxes: currentInvoice?.taxes || 0,
      status: !isCopy && currentInvoice?.status ? currentInvoice?.status : 'draft',
      discount: currentInvoice?.discount || 0,
      items: currentInvoice?.items.map((item) => ({
        ...item,
        service: item.service?.id || null,
      })) || [
        {
          title: '',
          description: '',
          service: '',
          quantity: 1,
          price: 0,
          total: 0,
        },
      ],
      subTotal: currentInvoice?.subTotal || 0,
      totalAmount: currentInvoice?.totalAmount || 0,
    }),
    [currentInvoice, isCopy, settings]
  );

  const methods = useForm({
    resolver: yupResolver(NewInvoiceSchema),
    defaultValues,
  });

  const {
    reset,
    handleSubmit,
    formState: { isSubmitting },
  } = methods;

  useEffect(() => {
    if (!settingsEmpty && !settingsLoading) {
      // eslint-disable-next-line react-hooks/exhaustive-deps
      reset(defaultValues);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [settingsEmpty, settingsLoading]);

  const handleSaveAsDraft = handleSubmit(async (data) => {
    loadingSave.onTrue();

    try {
      // generate collection id
      const id = generateId(collections.invoice);

      // attach serivce details
      const items = data.items.map((item) => ({
        ...item,
        service: invoiceServices.find((service) => service.id === item.service) || null,
      }));

      const currentTime = new Date();
      const createDateWithCurrentTime = new Date(data.createDate); // This creates a date object using the date from data.createDate
      // Set the time of createDateWithCurrentTime to the current hour, minutes, and seconds
      createDateWithCurrentTime.setHours(
        currentTime.getHours(),
        currentTime.getMinutes(),
        currentTime.getSeconds()
      );

      // transform data
      const writeData = {
        ...data,
        invoiceNumber: incrementInvoiceNumber(settings?.invoice.lastInvoiceNumber),
        status: 'draft',
        createDate: Timestamp.fromDate(createDateWithCurrentTime),
        dueDate: Timestamp.fromDate(new Date(data.dueDate)),
        items,
      };

      // upload invoice PDF to storage
      const invoicePdf = <InvoicePDF invoice={writeData as Invoice} currentStatus="pending" />;
      const blob: Blob = await pdf(invoicePdf).toBlob();
      const storagePath: string = `invoices/${writeData.invoiceTo.name}/${id}-${writeData.invoiceNumber}.pdf`;
      await uploadToFirebaseStorage(blob, storagePath);

      // write to DB
      await firestoreBatch([
        {
          docPath: `${collections.invoice}/${id}`,
          type: 'set',
          data: {
            ...writeData,
            pdfRef: storagePath,
          },
        },
        {
          docPath: `${collections.settings}/${documents.settingsInvoice}`,
          type: 'set',
          data: { lastInvoiceNumber: writeData.invoiceNumber },
        },
      ]);

      loadingSave.onFalse();
      router.push(paths.dashboard.invoice.root);
      // console.info('DATA', JSON.stringify(writeData, null, 2));
    } catch (error) {
      console.error(error);
      loadingSave.onFalse();
    }
  });

  const handleCreateAndUpdate = handleSubmit(async (data) => {
    loadingSend.onTrue();

    try {
      if (currentInvoice) {
        // attach serivce details
        const items = data.items.map((item) => ({
          ...item,
          service: invoiceServices.find((service) => service.id === item.service) || null,
        }));

        // transform data
        const writeData = {
          ...data,
          createDate: Timestamp.fromDate(new Date(data.createDate)),
          dueDate: Timestamp.fromDate(new Date(data.dueDate)),
          items,
        };

        // upload invoice PDF to storage
        const invoicePdf = <InvoicePDF invoice={writeData as Invoice} currentStatus="pending" />;
        const blob: Blob = await pdf(invoicePdf).toBlob();
        const storagePath: string = `invoices/${data.invoiceTo.name}/${currentInvoice.id}-${data.invoiceNumber}.pdf`;
        await uploadToFirebaseStorage(blob, storagePath);

        // update DB
        await updateDocument(collections.invoice, currentInvoice.id, {
          ...writeData,
          pdfRef: storagePath,
        });

        // mutate current data
        mutate([collections.invoice, currentInvoice.id]);
      } else {
        // generate collection id
        const id = generateId(collections.invoice);

        // attach serivce details
        const items = data.items.map((item) => ({
          ...item,
          service: invoiceServices?.find((service) => service.id === item.service) || null,
        }));

        // transform data
        const writeData = {
          ...data,
          createDate: Timestamp.fromDate(new Date(data.createDate)),
          dueDate: Timestamp.fromDate(new Date(data.dueDate)),
          items,
        };

        // upload invoice PDF to storage
        const invoicePdf = <InvoicePDF invoice={writeData as Invoice} currentStatus="pending" />;
        const blob: Blob = await pdf(invoicePdf).toBlob();
        const storagePath: string = `invoices/${data.invoiceTo.name}/${id}-${data.invoiceNumber}.pdf`;
        await uploadToFirebaseStorage(blob, storagePath);

        // write to DB
        await firestoreBatch([
          {
            docPath: `${collections.invoice}/${id}`,
            type: 'set',
            data: {
              ...writeData,
              pdfRef: storagePath,
            },
          },
          {
            docPath: `${collections.settings}/${documents.settingsInvoice}`,
            type: 'set',
            data: { lastInvoiceNumber: writeData.invoiceNumber },
          },
        ]);

        reset();
      }

      loadingSend.onFalse();
      router.push(paths.dashboard.invoice.root);

      // console.info('DATA', JSON.stringify(data, null, 2));
    } catch (error) {
      console.error(error);
      loadingSend.onFalse();
    }
  });

  return (
    <FormProvider methods={methods}>
      <Card>
        {settings?.company && <InvoiceNewEditAddress />}

        <InvoiceNewEditStatusDate isCopy={isCopy} />

        <InvoiceNewEditDetails />
      </Card>

      <Stack justifyContent="flex-end" direction="row" spacing={2} sx={{ mt: 3 }}>
        {currentInvoice && isCopy && (
          <LoadingButton
            color="inherit"
            size="large"
            variant="outlined"
            loading={loadingSave.value && isSubmitting}
            onClick={handleSaveAsDraft}
          >
            Save as Draft
          </LoadingButton>
        )}

        {!isCopy && (
          <LoadingButton
            size="large"
            variant="contained"
            loading={loadingSend.value && isSubmitting}
            onClick={handleCreateAndUpdate}
          >
            {currentInvoice ? 'Update' : 'Create'}
          </LoadingButton>
        )}
      </Stack>
    </FormProvider>
  );
}
