import type { TEditorRecipientsFormSchema } from '@documenso/lib/client-only/hooks/use-editor-recipients';
import type { TDetectedRecipientSchema } from '@documenso/lib/server-only/ai/envelope/detect-recipients/schema';
import { nanoid } from '@documenso/lib/universal/id';
import { isAssistantLastSigner, isCcRecipient } from '@documenso/lib/utils/recipients';
import { useToast } from '@documenso/ui/primitives/use-toast';
import type { DropResult } from '@hello-pangea/dnd';
import { plural } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { DocumentSigningOrder, RecipientRole } from '@prisma/client';
import { useCallback } from 'react';
import type { UseFormReturn } from 'react-hook-form';

type RecipientActionsOptions = {
  form: UseFormReturn<TEditorRecipientsFormSchema>;
  watchedSigners: TEditorRecipientsFormSchema['signers'];
  canRecipientBeModified: (recipientId?: number) => boolean;
  normalizeSigningOrders: (signers: TEditorRecipientsFormSchema['signers']) => TEditorRecipientsFormSchema['signers'];
  setShowSigningOrderConfirmation: (open: boolean) => void;
};

export const useEnvelopeRecipientActions = ({
  form,
  watchedSigners,
  canRecipientBeModified,
  normalizeSigningOrders,
  setShowSigningOrderConfirmation,
}: RecipientActionsOptions) => {
  const { t } = useLingui();
  const { toast } = useToast();
  const onAiDetectionComplete = (detectedRecipients: TDetectedRecipientSchema[]) => {
    const currentSigners = form.getValues('signers');

    let nextSigningOrder =
      currentSigners.length > 0 ? Math.max(...currentSigners.map((s) => s.signingOrder ?? 0)) + 1 : 1;

    // If the only signer is the default empty signer lets just replace it with the detected recipients
    if (currentSigners.length === 1 && !currentSigners[0].name && !currentSigners[0].email) {
      form.setValue(
        'signers',
        detectedRecipients.map((recipient, index) => ({
          formId: nanoid(12),
          name: recipient.name,
          email: recipient.email,
          role: recipient.role,
          actionAuth: [],
          signingOrder: index + 1,
        })),
        {
          shouldValidate: true,
          shouldDirty: true,
        },
      );

      return;
    }

    for (const recipient of detectedRecipients) {
      const emailExists = currentSigners.some((s) => s.email.toLowerCase() === recipient.email.toLowerCase());

      const nameExists = currentSigners.some((s) => s.name.toLowerCase() === recipient.name.toLowerCase());

      if ((emailExists && recipient.email) || (nameExists && recipient.name)) {
        continue;
      }

      currentSigners.push({
        formId: nanoid(12),
        name: recipient.name,
        email: recipient.email,
        role: recipient.role,
        actionAuth: [],
        signingOrder: nextSigningOrder,
      });

      nextSigningOrder += 1;
    }

    form.setValue('signers', normalizeSigningOrders(currentSigners), {
      shouldValidate: true,
      shouldDirty: true,
    });

    toast({
      title: plural(detectedRecipients.length, {
        one: `Recipient added`,
        other: `Recipients added`,
      }),
      description: plural(detectedRecipients.length, {
        one: `# recipient have been added from AI detection.`,
        other: `# recipients have been added from AI detection.`,
      }),
    });
  };

  const onDragEnd = useCallback(
    async (result: DropResult) => {
      if (!result.destination) {
        return;
      }

      const items = Array.from(watchedSigners);
      const [reorderedSigner] = items.splice(result.source.index, 1);

      // Find next valid position
      let insertIndex = result.destination.index;
      while (insertIndex < items.length && !canRecipientBeModified(items[insertIndex].id)) {
        insertIndex++;
      }

      items.splice(insertIndex, 0, reorderedSigner);

      const updatedSigners = normalizeSigningOrders(items);

      form.setValue('signers', updatedSigners, {
        shouldValidate: true,
        shouldDirty: true,
      });

      if (isAssistantLastSigner(updatedSigners)) {
        toast({
          title: t`Warning: Assistant as last signer`,
          description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
        });
      }

      await form.trigger('signers');
    },
    [form, canRecipientBeModified, watchedSigners, toast],
  );

  const handleRoleChange = useCallback(
    (index: number, role: RecipientRole) => {
      const currentSigners = form.getValues('signers');
      const signingOrder = form.getValues('signingOrder');

      // Handle parallel to sequential conversion for assistants
      if (role === RecipientRole.ASSISTANT && signingOrder === DocumentSigningOrder.PARALLEL) {
        form.setValue('signingOrder', DocumentSigningOrder.SEQUENTIAL, {
          shouldValidate: true,
          shouldDirty: true,
        });
        toast({
          title: t`Signing order is enabled.`,
          description: t`You cannot add assistants when signing order is disabled.`,
          variant: 'destructive',
        });
        return;
      }

      const updatedSigners = normalizeSigningOrders(
        currentSigners.map((signer, idx) => ({
          ...signer,
          role: idx === index ? role : signer.role,
        })),
      );

      form.setValue('signers', updatedSigners, {
        shouldValidate: true,
        shouldDirty: true,
      });

      if (role === RecipientRole.ASSISTANT && isAssistantLastSigner(updatedSigners)) {
        toast({
          title: t`Warning: Assistant as last signer`,
          description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
        });
      }
    },
    [form, toast, canRecipientBeModified],
  );

  const handleSigningOrderChange = useCallback(
    (index: number, newOrderString: string) => {
      const trimmedOrderString = newOrderString.trim();
      if (!trimmedOrderString) {
        return;
      }

      const newOrder = Number(trimmedOrderString);
      if (!Number.isInteger(newOrder) || newOrder < 1) {
        return;
      }

      const currentSigners = form.getValues('signers');
      const signer = currentSigners[index];

      if (isCcRecipient(signer)) {
        return;
      }

      const nonCcSigners = currentSigners.filter((s) => !isCcRecipient(s));
      const ccSigners = currentSigners.filter((s) => isCcRecipient(s));
      const currentSigningOrderIndex = nonCcSigners.findIndex((s) => s.formId === signer.formId);

      if (currentSigningOrderIndex === -1) {
        return;
      }

      const [reorderedSigner] = nonCcSigners.splice(currentSigningOrderIndex, 1);
      const newPosition = Math.min(Math.max(0, newOrder - 1), nonCcSigners.length);
      nonCcSigners.splice(newPosition, 0, reorderedSigner);

      const updatedSigners = normalizeSigningOrders([...nonCcSigners, ...ccSigners]);

      form.setValue('signers', updatedSigners, {
        shouldValidate: true,
        shouldDirty: true,
      });

      if (signer.role === RecipientRole.ASSISTANT && isAssistantLastSigner(updatedSigners)) {
        toast({
          title: t`Warning: Assistant as last signer`,
          description: t`Having an assistant as the last signer means they will be unable to take any action as there are no subsequent signers to assist.`,
        });
      }
    },
    [form, canRecipientBeModified, toast],
  );

  const handleSigningOrderDisable = useCallback(() => {
    setShowSigningOrderConfirmation(false);

    const currentSigners = form.getValues('signers');
    const updatedSigners = normalizeSigningOrders(
      currentSigners.map((signer) => ({
        ...signer,
        role: signer.role === RecipientRole.ASSISTANT ? RecipientRole.SIGNER : signer.role,
      })),
    );

    form.setValue('signers', updatedSigners, {
      shouldValidate: true,
      shouldDirty: true,
    });
    form.setValue('signingOrder', DocumentSigningOrder.PARALLEL, {
      shouldValidate: true,
      shouldDirty: true,
    });
    form.setValue('allowDictateNextSigner', false, {
      shouldValidate: true,
      shouldDirty: true,
    });

    void form.trigger();
  }, [form]);

  return { onAiDetectionComplete, onDragEnd, handleRoleChange, handleSigningOrderChange, handleSigningOrderDisable };
};
