import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
import type { DocumentAndSender } from '@documenso/lib/server-only/document/get-document-by-token';
import type { TRecipientAccessAuth } from '@documenso/lib/types/document-auth';
import {
  ZCheckboxFieldMeta,
  ZDropdownFieldMeta,
  ZNumberFieldMeta,
  ZRadioFieldMeta,
  ZTextFieldMeta,
} from '@documenso/lib/types/field-meta';
import type { CompletedField } from '@documenso/lib/types/fields';
import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers';
import { getDocumentDataUrlForPdfViewer } from '@documenso/lib/utils/envelope-download';
import { validateFieldsInserted } from '@documenso/lib/utils/fields';
import type { FieldWithSignatureAndFieldMeta } from '@documenso/prisma/types/field-with-signature-and-fieldmeta';
import type { RecipientWithFields } from '@documenso/prisma/types/recipient-with-fields';
import { trpc } from '@documenso/trpc/react';
import { DocumentReadOnlyFields } from '@documenso/ui/components/document/document-read-only-fields';
import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { Trans } from '@lingui/react/macro';
import type { Field } from '@prisma/client';
import { FieldType, RecipientRole } from '@prisma/client';
import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
import { useMemo, useState } from 'react';
import { match, P } from 'ts-pattern';

import { DocumentSigningAttachmentsPopover } from '~/components/general/document-signing/document-signing-attachments-popover';
import { DocumentSigningAutoSign } from '~/components/general/document-signing/document-signing-auto-sign';
import { DocumentSigningCheckboxField } from '~/components/general/document-signing/document-signing-checkbox-field';
import { DocumentSigningDateField } from '~/components/general/document-signing/document-signing-date-field';
import { DocumentSigningDropdownField } from '~/components/general/document-signing/document-signing-dropdown-field';
import { DocumentSigningEmailField } from '~/components/general/document-signing/document-signing-email-field';
import { DocumentSigningForm } from '~/components/general/document-signing/document-signing-form';
import { DocumentSigningInitialsField } from '~/components/general/document-signing/document-signing-initials-field';
import { DocumentSigningNameField } from '~/components/general/document-signing/document-signing-name-field';
import { DocumentSigningNumberField } from '~/components/general/document-signing/document-signing-number-field';
import { DocumentSigningRadioField } from '~/components/general/document-signing/document-signing-radio-field';
import { DocumentSigningRejectDialog } from '~/components/general/document-signing/document-signing-reject-dialog';
import { DocumentSigningSignatureField } from '~/components/general/document-signing/document-signing-signature-field';
import { DocumentSigningTextField } from '~/components/general/document-signing/document-signing-text-field';
import PDFViewerLazy from '~/components/general/pdf-viewer/pdf-viewer-lazy';

import { useRequiredDocumentSigningAuthContext } from './document-signing-auth-provider';
import { DocumentSigningCompleteDialog } from './document-signing-complete-dialog';
import { DocumentSigningRecipientProvider } from './document-signing-recipient-provider';

type DocumentSigningBranding = {
  brandingEnabled: boolean;
  brandingLogo: string;
};

export type DocumentSigningPageViewV1Props = {
  recipient: RecipientWithFields;
  document: DocumentAndSender;
  fields: Field[];
  completedFields: CompletedField[];
  isRecipientsTurn: boolean;
  allRecipients?: RecipientWithFields[];
  branding: DocumentSigningBranding;
  includeSenderDetails: boolean;
};

export const DocumentSigningPageViewV1 = ({
  recipient,
  document,
  fields,
  completedFields,
  isRecipientsTurn,
  allRecipients = [],
  includeSenderDetails,
  branding,
}: DocumentSigningPageViewV1Props) => {
  const { documentData, documentMeta } = document;

  const { derivedRecipientAccessAuth, user: authUser } = useRequiredDocumentSigningAuthContext();

  const hasAuthenticator = authUser?.twoFactorEnabled
    ? authUser.twoFactorEnabled && authUser.email === recipient.email
    : false;

  const analytics = useAnalytics();

  const [selectedSignerId, setSelectedSignerId] = useState<number | null>(allRecipients?.[0]?.id);
  const [isExpanded, setIsExpanded] = useState(false);

  const {
    mutateAsync: completeDocumentWithToken,
    isPending,
    isSuccess,
  } = trpc.recipient.completeDocumentWithToken.useMutation();

  // Keep the loading state going if successful since the redirect may take some time.
  const isSubmitting = isPending || isSuccess;

  const fieldsRequiringValidation = useMemo(() => fields.filter(isFieldUnsignedAndRequired), [fields]);

  const fieldsValidated = () => {
    validateFieldsInserted(fieldsRequiringValidation);
  };

  const completeDocument = async (options: {
    accessAuthOptions?: TRecipientAccessAuth;
    nextSigner?: { email: string; name: string };
  }) => {
    const { accessAuthOptions, nextSigner } = options;

    const payload = {
      token: recipient.token,
      documentId: document.id,
      accessAuthOptions,
      ...(nextSigner?.email && nextSigner?.name ? { nextSigner } : {}),
    };

    await completeDocumentWithToken(payload);

    analytics.capture('App: Recipient has completed signing', {
      signerId: recipient.id,
      documentId: document.id,
      timestamp: new Date().toISOString(),
    });

    if (documentMeta?.redirectUrl) {
      window.location.href = documentMeta.redirectUrl;
    } else {
      window.location.href = `/sign/${recipient.token}/complete`;
    }
  };

  let senderName = document.user.name ?? '';
  let senderEmail = `(${document.user.email})`;

  if (includeSenderDetails) {
    senderName = document.team?.name ?? '';
    senderEmail = document.team?.teamEmail?.email ? `(${document.team.teamEmail.email})` : '';
  }

  const selectedSigner = allRecipients?.find((r) => r.id === selectedSignerId);
  const targetSigner = recipient.role === RecipientRole.ASSISTANT && selectedSigner ? selectedSigner : null;

  const nextRecipient = useMemo(() => {
    if (!documentMeta?.signingOrder || documentMeta.signingOrder !== 'SEQUENTIAL') {
      return undefined;
    }

    const sortedRecipients = [...allRecipients].sort((a, b) => {
      // Sort by signingOrder first (nulls last), then by id
      if (a.signingOrder === null && b.signingOrder === null) {
        return a.id - b.id;
      }
      if (a.signingOrder === null) {
        return 1;
      }
      if (b.signingOrder === null) {
        return -1;
      }
      if (a.signingOrder === b.signingOrder) {
        return a.id - b.id;
      }
      return a.signingOrder - b.signingOrder;
    });

    const currentIndex = sortedRecipients.findIndex((r) => r.id === recipient.id);
    return currentIndex !== -1 && currentIndex < sortedRecipients.length - 1
      ? sortedRecipients[currentIndex + 1]
      : undefined;
  }, [document.documentMeta?.signingOrder, allRecipients, recipient.id]);

  const pendingFields = fieldsRequiringValidation.filter((field) => !field.inserted);
  const hasPendingFields = pendingFields.length > 0;

  const hasCustomBrandingLogo = branding.brandingEnabled && Boolean(branding.brandingLogo);

  return (
    <DocumentSigningRecipientProvider recipient={recipient} targetSigner={targetSigner}>
      <div className="mx-auto w-full max-w-screen-xl sm:px-6">
        {hasCustomBrandingLogo && (
          <img
            src={`/api/branding/logo/team/${document.teamId}`}
            alt={`${document.team.name}'s Logo`}
            className="mb-4 h-12 w-12 md:mb-2"
          />
        )}
        <h1
          className="block max-w-[20rem] truncate font-semibold text-2xl sm:mt-4 md:max-w-[30rem] md:text-3xl"
          title={document.title}
        >
          {document.title}
        </h1>

        <div className="mt-1.5 flex flex-wrap items-center justify-between gap-y-2 sm:mt-2.5 sm:gap-y-0">
          <div className="max-w-[50ch]">
            <span className="text-muted-foreground">
              {match(recipient.role)
                .with(RecipientRole.VIEWER, () =>
                  includeSenderDetails ? (
                    <Trans>
                      <span className="truncate" title={senderName}>
                        {senderName} {senderEmail}
                      </span>{' '}
                      on behalf of "{document.team?.name}" has invited you to view this document
                    </Trans>
                  ) : (
                    <Trans>
                      <span className="truncate" title={senderName}>
                        {senderName} {senderEmail}
                      </span>{' '}
                      has invited you to view this document
                    </Trans>
                  ),
                )
                .with(RecipientRole.SIGNER, () =>
                  includeSenderDetails ? (
                    <Trans>
                      <span className="truncate" title={senderName}>
                        {senderName} {senderEmail}
                      </span>{' '}
                      on behalf of "{document.team?.name}" has invited you to sign this document
                    </Trans>
                  ) : (
                    <Trans>
                      <span className="truncate" title={senderName}>
                        {senderName} {senderEmail}
                      </span>{' '}
                      has invited you to sign this document
                    </Trans>
                  ),
                )
                .with(RecipientRole.APPROVER, () =>
                  includeSenderDetails ? (
                    <Trans>
                      <span className="truncate" title={senderName}>
                        {senderName} {senderEmail}
                      </span>{' '}
                      on behalf of "{document.team?.name}" has invited you to approve this document
                    </Trans>
                  ) : (
                    <Trans>
                      <span className="truncate" title={senderName}>
                        {senderName} {senderEmail}
                      </span>{' '}
                      has invited you to approve this document
                    </Trans>
                  ),
                )
                .with(RecipientRole.ASSISTANT, () =>
                  includeSenderDetails ? (
                    <Trans>
                      <span className="truncate" title={senderName}>
                        {senderName} {senderEmail}
                      </span>{' '}
                      on behalf of "{document.team?.name}" has invited you to assist this document
                    </Trans>
                  ) : (
                    <Trans>
                      <span className="truncate" title={senderName}>
                        {senderName} {senderEmail}
                      </span>{' '}
                      has invited you to assist this document
                    </Trans>
                  ),
                )
                .otherwise(() => null)}
            </span>
          </div>

          <div className="flex items-center gap-x-4">
            <DocumentSigningAttachmentsPopover envelopeId={document.envelopeId} token={recipient.token} />
            <DocumentSigningRejectDialog documentId={document.id} token={recipient.token} />
          </div>
        </div>

        <div className="relative mt-4 flex w-full flex-col gap-x-6 gap-y-8 sm:mt-8 md:flex-row lg:gap-x-8 lg:gap-y-0">
          <div className="flex-1">
            <Card className="rounded-xl before:rounded-xl" gradient>
              <CardContent className="p-2">
                <PDFViewerLazy
                  key={document.envelopeItems[0]?.id}
                  data={getDocumentDataUrlForPdfViewer({
                    envelopeId: document.envelopeId,
                    envelopeItemId: document.envelopeItems[0]?.id,
                    documentDataId: document.envelopeItems[0]?.documentData.id,
                    version: 'current',
                    token: recipient.token,
                    presignToken: undefined,
                  })}
                  scrollParentRef="window"
                />
              </CardContent>
            </Card>
          </div>

          <div
            key={isExpanded ? 'expanded' : 'collapsed'}
            className="group/document-widget fixed bottom-6 left-0 z-50 h-fit max-h-[calc(100dvh-2rem)] w-full flex-shrink-0 px-4 md:sticky md:top-4 md:bottom-[unset] md:z-auto md:w-[350px] md:px-0"
            data-expanded={isExpanded || undefined}
          >
            <div className="flex w-full flex-col rounded-xl border border-border bg-widget px-4 py-4 md:py-6">
              <div className="flex items-center justify-between gap-x-2">
                <h3 className="font-semibold text-foreground text-xl md:text-2xl">
                  {match(recipient.role)
                    .with(RecipientRole.VIEWER, () => <Trans>View Document</Trans>)
                    .with(RecipientRole.SIGNER, () => <Trans>Sign Document</Trans>)
                    .with(RecipientRole.APPROVER, () => <Trans>Approve Document</Trans>)
                    .with(RecipientRole.ASSISTANT, () => <Trans>Assist Document</Trans>)
                    .otherwise(() => null)}
                </h3>

                {match({ hasPendingFields, isExpanded, role: recipient.role })
                  .with(
                    {
                      hasPendingFields: false,
                      role: P.not(RecipientRole.ASSISTANT),
                      isExpanded: false,
                    },
                    () => (
                      <div className="md:hidden">
                        <DocumentSigningCompleteDialog
                          isSubmitting={isSubmitting}
                          documentTitle={document.title}
                          fields={fields}
                          fieldsValidated={fieldsValidated}
                          disabled={!isRecipientsTurn}
                          onSignatureComplete={async (nextSigner) => completeDocument({ nextSigner })}
                          recipient={recipient}
                          allowDictateNextSigner={nextRecipient && documentMeta?.allowDictateNextSigner}
                          defaultNextSigner={
                            nextRecipient ? { name: nextRecipient.name, email: nextRecipient.email } : undefined
                          }
                        />
                      </div>
                    ),
                  )
                  .with({ isExpanded: true }, () => (
                    <Button
                      variant="outline"
                      className="h-8 w-8 bg-background p-0 md:hidden dark:bg-foreground"
                      onClick={() => setIsExpanded(false)}
                    >
                      <LucideChevronDown className="h-5 w-5 text-muted-foreground dark:text-background" />
                    </Button>
                  ))
                  .otherwise(() => (
                    <Button
                      variant="outline"
                      className="h-8 w-8 bg-background p-0 md:hidden dark:bg-foreground"
                      onClick={() => setIsExpanded(true)}
                    >
                      <LucideChevronUp className="h-5 w-5 text-muted-foreground dark:text-background" />
                    </Button>
                  ))}
              </div>

              <div className="hidden group-data-[expanded]/document-widget:block md:block">
                <p className="mt-2 text-muted-foreground text-sm">
                  {match(recipient.role)
                    .with(RecipientRole.VIEWER, () => <Trans>Please mark as viewed to complete.</Trans>)
                    .with(RecipientRole.SIGNER, () => <Trans>Please review the document before signing.</Trans>)
                    .with(RecipientRole.APPROVER, () => <Trans>Please review the document before approving.</Trans>)
                    .with(RecipientRole.ASSISTANT, () => <Trans>Complete the fields for the following signers.</Trans>)
                    .otherwise(() => null)}
                </p>

                <hr className="mt-4 mb-8 border-border" />
              </div>

              <div className="-mx-2 hidden px-2 group-data-[expanded]/document-widget:block md:block">
                <DocumentSigningForm
                  document={document}
                  recipient={recipient}
                  fields={fields}
                  isRecipientsTurn={isRecipientsTurn}
                  allRecipients={allRecipients}
                  setSelectedSignerId={setSelectedSignerId}
                  completeDocument={completeDocument}
                  isSubmitting={isSubmitting}
                  fieldsValidated={fieldsValidated}
                  nextRecipient={nextRecipient}
                />
              </div>
            </div>
          </div>
        </div>

        <DocumentReadOnlyFields
          documentMeta={documentMeta || undefined}
          fields={completedFields}
          showRecipientTooltip={true}
        />

        {recipient.role !== RecipientRole.ASSISTANT && (
          <DocumentSigningAutoSign recipient={recipient} fields={fields} />
        )}

        <ElementVisible target={PDF_VIEWER_PAGE_SELECTOR}>
          {fields
            .filter((field) => recipient.role !== RecipientRole.ASSISTANT || field.recipientId === selectedSigner?.id)
            .map((field) =>
              match(field.type)
                .with(FieldType.SIGNATURE, () => (
                  <DocumentSigningSignatureField
                    key={field.id}
                    field={field}
                    typedSignatureEnabled={documentMeta?.typedSignatureEnabled}
                    uploadSignatureEnabled={documentMeta?.uploadSignatureEnabled}
                    drawSignatureEnabled={documentMeta?.drawSignatureEnabled}
                  />
                ))
                .with(FieldType.INITIALS, () => <DocumentSigningInitialsField key={field.id} field={field} />)
                .with(FieldType.NAME, () => <DocumentSigningNameField key={field.id} field={field} />)
                .with(FieldType.DATE, () => (
                  <DocumentSigningDateField
                    key={field.id}
                    field={field}
                    dateFormat={documentMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT}
                    timezone={documentMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE}
                  />
                ))
                .with(FieldType.EMAIL, () => <DocumentSigningEmailField key={field.id} field={field} />)
                .with(FieldType.TEXT, () => {
                  const fieldWithMeta: FieldWithSignatureAndFieldMeta = {
                    ...field,
                    fieldMeta: field.fieldMeta ? ZTextFieldMeta.parse(field.fieldMeta) : null,
                  };
                  return <DocumentSigningTextField key={field.id} field={fieldWithMeta} />;
                })
                .with(FieldType.NUMBER, () => {
                  const fieldWithMeta: FieldWithSignatureAndFieldMeta = {
                    ...field,
                    fieldMeta: field.fieldMeta ? ZNumberFieldMeta.parse(field.fieldMeta) : null,
                  };
                  return <DocumentSigningNumberField key={field.id} field={fieldWithMeta} />;
                })
                .with(FieldType.RADIO, () => {
                  const fieldWithMeta: FieldWithSignatureAndFieldMeta = {
                    ...field,
                    fieldMeta: field.fieldMeta ? ZRadioFieldMeta.parse(field.fieldMeta) : null,
                  };
                  return <DocumentSigningRadioField key={field.id} field={fieldWithMeta} />;
                })
                .with(FieldType.CHECKBOX, () => {
                  const fieldWithMeta: FieldWithSignatureAndFieldMeta = {
                    ...field,
                    fieldMeta: field.fieldMeta ? ZCheckboxFieldMeta.parse(field.fieldMeta) : null,
                  };
                  return <DocumentSigningCheckboxField key={field.id} field={fieldWithMeta} />;
                })
                .with(FieldType.DROPDOWN, () => {
                  const fieldWithMeta: FieldWithSignatureAndFieldMeta = {
                    ...field,
                    fieldMeta: field.fieldMeta ? ZDropdownFieldMeta.parse(field.fieldMeta) : null,
                  };
                  return <DocumentSigningDropdownField key={field.id} field={fieldWithMeta} />;
                })
                .otherwise(() => null),
            )}
        </ElementVisible>
      </div>
    </DocumentSigningRecipientProvider>
  );
};
