import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { useCurrentEnvelopeRender } from '@documenso/lib/client-only/providers/envelope-render-provider';
import { PDF_VIEWER_ERROR_MESSAGES } from '@documenso/lib/constants/pdf-viewer-i18n';
import type { NormalizedFieldWithContext } from '@documenso/lib/server-only/ai/envelope/detect-fields/types';
import {
  FIELD_META_DEFAULT_VALUES,
  type TCheckboxFieldMeta,
  type TDateFieldMeta,
  type TDropdownFieldMeta,
  type TEmailFieldMeta,
  type TFieldMetaSchema,
  type TInitialsFieldMeta,
  type TNameFieldMeta,
  type TNumberFieldMeta,
  type TRadioFieldMeta,
  type TSignatureFieldMeta,
  type TTextFieldMeta,
} from '@documenso/lib/types/field-meta';
import { getEnvelopeItemPermissions } from '@documenso/lib/utils/envelope';
import { getOverlappingFieldPairs } from '@documenso/lib/utils/fields-overlap';
import { canRecipientFieldsBeModified } from '@documenso/lib/utils/recipients';
import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out';
import { cn } from '@documenso/ui/lib/utils';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button';
import { Separator } from '@documenso/ui/primitives/separator';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { DocumentStatus, FieldType, RecipientRole } from '@prisma/client';
import { AlertTriangleIcon, FileTextIcon, PencilIcon, SparklesIcon } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useRevalidator, useSearchParams } from 'react-router';
import { isDeepEqual } from 'remeda';
import { match } from 'ts-pattern';

import { AiFeaturesEnableDialog } from '~/components/dialogs/ai-features-enable-dialog';
import { AiFieldDetectionDialog } from '~/components/dialogs/ai-field-detection-dialog';
import { EnvelopeItemEditDialog } from '~/components/dialogs/envelope-item-edit-dialog';
import { EditorFieldCheckboxForm } from '~/components/forms/editor/editor-field-checkbox-form';
import { EditorFieldDateForm } from '~/components/forms/editor/editor-field-date-form';
import { EditorFieldDropdownForm } from '~/components/forms/editor/editor-field-dropdown-form';
import { EditorFieldEmailForm } from '~/components/forms/editor/editor-field-email-form';
import { EditorFieldInitialsForm } from '~/components/forms/editor/editor-field-initials-form';
import { EditorFieldNameForm } from '~/components/forms/editor/editor-field-name-form';
import { EditorFieldNumberForm } from '~/components/forms/editor/editor-field-number-form';
import { EditorFieldRadioForm } from '~/components/forms/editor/editor-field-radio-form';
import { EditorFieldSignatureForm } from '~/components/forms/editor/editor-field-signature-form';
import { EditorFieldTextForm } from '~/components/forms/editor/editor-field-text-form';
import { EnvelopePdfViewer } from '~/components/general/pdf-viewer/envelope-pdf-viewer';
import { useCurrentTeam } from '~/providers/team';
import { EnvelopeEditorFieldDiagnostics } from './envelope-editor-field-diagnostics';
import { EnvelopeEditorFieldDragDrop } from './envelope-editor-fields-drag-drop';
import { EnvelopeEditorFieldsPageRenderer } from './envelope-editor-fields-page-renderer';
import { EnvelopeEditorInvalidDirectTemplateAlert } from './envelope-editor-invalid-direct-template-alert';
import { EnvelopeEditorPanel } from './envelope-editor-panel';
import { EnvelopeRendererFileSelector } from './envelope-file-selector';
import { EnvelopeRecipientSelector } from './envelope-recipient-selector';

const FieldSettingsTypeTranslations: Record<FieldType, MessageDescriptor> = {
  [FieldType.SIGNATURE]: msg`Signature Settings`,
  [FieldType.FREE_SIGNATURE]: msg`Free Signature Settings`,
  [FieldType.TEXT]: msg`Text Settings`,
  [FieldType.DATE]: msg`Date Settings`,
  [FieldType.EMAIL]: msg`Email Settings`,
  [FieldType.NAME]: msg`Name Settings`,
  [FieldType.INITIALS]: msg`Initials Settings`,
  [FieldType.NUMBER]: msg`Number Settings`,
  [FieldType.RADIO]: msg`Radio Settings`,
  [FieldType.CHECKBOX]: msg`Checkbox Settings`,
  [FieldType.DROPDOWN]: msg`Dropdown Settings`,
};

export const EnvelopeEditorFieldsPage = () => {
  const [searchParams] = useSearchParams();

  const team = useCurrentTeam();

  const scrollableContainerRef = useRef<HTMLDivElement>(null);

  const { envelope, editorFields, navigateToStep, editorConfig } = useCurrentEnvelopeEditor();

  const { currentEnvelopeItem, setCurrentEnvelopeItem } = useCurrentEnvelopeRender();

  const { _ } = useLingui();

  const [isAiFieldDialogOpen, setIsAiFieldDialogOpen] = useState(false);
  const [isAiEnableDialogOpen, setIsAiEnableDialogOpen] = useState(false);
  const { revalidate } = useRevalidator();

  const envelopeItemPermissions = useMemo(
    () => getEnvelopeItemPermissions(envelope, envelope.recipients),
    [envelope, envelope.recipients],
  );

  const selectedField = useMemo(() => structuredClone(editorFields.selectedField), [editorFields.selectedField]);

  /**
   * Debounce the fields used for overlap detection so we don't recompute on every
   * small drag/resize movement, which is expensive on large field counts and can
   * bog down lower-end devices.
   */
  const debouncedLocalFields = useDebouncedValue(editorFields.localFields, 300);

  /**
   * Fields that significantly overlap each other. Overlapping fields render poorly in
   * the editor and can behave unexpectedly during signing, so we warn the author here.
   */
  const overlappingFieldPairs = useMemo(
    () =>
      getOverlappingFieldPairs(
        debouncedLocalFields.map((field) => ({
          id: field.formId,
          envelopeItemId: field.envelopeItemId,
          page: field.page,
          positionX: field.positionX,
          positionY: field.positionY,
          width: field.width,
          height: field.height,
        })),
      ),
    [debouncedLocalFields],
  );

  const handleReviewOverlappingField = () => {
    const firstPair = overlappingFieldPairs[0];

    if (!firstPair) {
      return;
    }

    const targetField = editorFields.localFields.find((field) => field.formId === firstPair.fieldA.id);

    if (!targetField) {
      return;
    }

    if (targetField.envelopeItemId !== currentEnvelopeItem?.id) {
      setCurrentEnvelopeItem(targetField.envelopeItemId);
    }

    editorFields.setSelectedField(targetField.formId);
  };

  const updateSelectedFieldMeta = (fieldMeta: TFieldMetaSchema) => {
    if (!selectedField) {
      return;
    }

    const isMetaSame = isDeepEqual(selectedField.fieldMeta, fieldMeta);

    if (!isMetaSame) {
      editorFields.updateFieldByFormId(selectedField.formId, {
        fieldMeta,
      });
    }
  };

  const onFieldDetectionComplete = (fields: NormalizedFieldWithContext[]) => {
    for (const field of fields) {
      editorFields.addField({
        height: field.height,
        width: field.width,
        positionX: field.positionX,
        positionY: field.positionY,
        type: field.type,
        envelopeItemId: field.envelopeItemId,
        recipientId: field.recipientId,
        page: field.pageNumber,
        fieldMeta: structuredClone(FIELD_META_DEFAULT_VALUES[field.type]),
      });
    }

    setIsAiFieldDialogOpen(false);
  };

  /**
   * Set the selected recipient to the first recipient in the envelope.
   */
  useEffect(() => {
    const firstSelectableRecipient = envelope.recipients.find(
      (recipient) => recipient.role === RecipientRole.SIGNER || recipient.role === RecipientRole.APPROVER,
    );

    editorFields.setSelectedRecipient(firstSelectableRecipient?.id ?? null);
  }, []);

  const onDetectClick = () => {
    if (!team.preferences.aiFeaturesEnabled) {
      setIsAiEnableDialogOpen(true);
      return;
    }

    setIsAiFieldDialogOpen(true);
  };

  const onAiFeaturesEnabled = () => {
    void revalidate().then(() => {
      setIsAiEnableDialogOpen(false);
      setIsAiFieldDialogOpen(true);
    });
  };

  return (
    <div className="relative flex h-full min-h-0 min-w-0 flex-col lg:flex-row">
      <div className="flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto px-2" ref={scrollableContainerRef}>
        {/* Horizontal envelope item selector */}
        <EnvelopeRendererFileSelector
          className="px-0"
          fields={editorFields.localFields}
          renderItemAction={
            editorConfig.envelopeItems !== null &&
            editorConfig.envelopeItems.allowReplace &&
            envelopeItemPermissions.canFileBeChanged
              ? (item) => (
                  <div className="relative flex h-5 w-5 flex-shrink-0 items-center justify-center">
                    <div
                      className={cn('h-2 w-2 rounded-full transition-opacity duration-150 group-hover:opacity-0', {
                        'bg-primary': currentEnvelopeItem?.id === item.id,
                      })}
                    />
                    <EnvelopeItemEditDialog
                      envelopeItem={item}
                      allowConfigureTitle={editorConfig.envelopeItems?.allowConfigureTitle ?? false}
                      trigger={
                        <span
                          className="absolute inset-0 flex cursor-pointer items-center justify-center opacity-0 transition-opacity duration-150 group-hover:opacity-100"
                          onClick={(e) => e.stopPropagation()}
                          data-testid={`envelope-item-edit-button-${item.id}`}
                        >
                          <PencilIcon className="h-3.5 w-3.5" />
                        </span>
                      }
                    />
                  </div>
                )
              : undefined
          }
        />

        <EnvelopeEditorInvalidDirectTemplateAlert />

        {/* Document View */}
        <div className="mt-4 flex h-full flex-col items-center justify-center">
          {envelope.recipients.length === 0 && (
            <Alert
              variant="neutral"
              className="mb-4 flex max-w-[800px] flex-row items-center justify-between space-y-0 rounded-sm border border-border bg-background"
            >
              <div className="flex flex-col gap-1">
                <AlertTitle>
                  <Trans>Missing Recipients</Trans>
                </AlertTitle>
                <AlertDescription>
                  <Trans>You need at least one recipient to add fields</Trans>
                </AlertDescription>
              </div>

              <Button variant="outline" onClick={() => void navigateToStep('upload')}>
                <Trans>Add Recipients</Trans>
              </Button>
            </Alert>
          )}

          {overlappingFieldPairs.length > 0 && (
            <Alert
              variant="warning"
              className="mt-20 mb-4 flex w-full max-w-[800px] flex-row items-center justify-between space-y-0 rounded-sm"
            >
              <div className="flex flex-row items-start gap-3">
                <AlertTriangleIcon className="mt-0.5 h-5 w-5 flex-shrink-0" />

                <div className="flex flex-col gap-1">
                  <AlertTitle>
                    <Trans>Overlapping fields detected</Trans>
                  </AlertTitle>
                  <AlertDescription>
                    <Trans>
                      Some fields are placed on top of each other. This may complicate the signing process or cause
                      fields to not work as expected.
                    </Trans>
                  </AlertDescription>
                </div>
              </div>
            </Alert>
          )}

          {currentEnvelopeItem !== null ? (
            <EnvelopePdfViewer
              customPageRenderer={EnvelopeEditorFieldsPageRenderer}
              scrollParentRef={scrollableContainerRef}
              errorMessage={PDF_VIEWER_ERROR_MESSAGES.editor}
            />
          ) : (
            <div className="flex flex-col items-center justify-center py-32">
              <FileTextIcon className="h-10 w-10 text-muted-foreground" />
              <p className="mt-1 text-foreground text-sm">
                <Trans>No documents found</Trans>
              </p>
              <p className="mt-1 text-muted-foreground text-sm">
                <Trans>Please upload a document to continue</Trans>
              </p>
            </div>
          )}
        </div>
      </div>

      {/* Right Section - Form Fields Panel */}
      {currentEnvelopeItem && envelope.recipients.length > 0 && (
        <EnvelopeEditorPanel
          title={<Trans>Add Fields</Trans>}
          side="right"
          revealKey={selectedField?.formId}
          testId="envelope-editor-fields-panel"
        >
          <div className="py-4">
            {/* Recipient selector section. */}
            <section className="px-4">
              <h3 className="mb-2 font-semibold text-foreground text-sm">
                <Trans>Selected Recipient</Trans>
              </h3>

              <EnvelopeRecipientSelector
                selectedRecipient={editorFields.selectedRecipient}
                onSelectedRecipientChange={(recipient) => editorFields.changeSelectedRecipient(recipient.id)}
                recipients={envelope.recipients}
                fields={envelope.fields}
                className="w-full"
                align="end"
              />

              {editorFields.selectedRecipient &&
                !canRecipientFieldsBeModified(editorFields.selectedRecipient, envelope.fields) && (
                  <Alert className="mt-4" variant="warning">
                    <AlertDescription>
                      <Trans>
                        This recipient can no longer be modified as they have signed a field, or completed the document.
                      </Trans>
                    </AlertDescription>
                  </Alert>
                )}
            </section>

            <Separator className="my-4" />

            {/* Add fields section. */}
            <section className="px-4">
              <h3 className="mb-2 font-semibold text-foreground text-sm">
                <Trans>Add Fields</Trans>
              </h3>

              <EnvelopeEditorFieldDragDrop
                selectedRecipientId={editorFields.selectedRecipient?.id ?? null}
                selectedEnvelopeItemId={currentEnvelopeItem?.id ?? null}
              />

              {editorConfig.fields?.allowAIDetection && (
                <>
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    className="mt-4 w-full"
                    onClick={onDetectClick}
                    disabled={envelope.status !== DocumentStatus.DRAFT}
                    title={
                      envelope.status !== DocumentStatus.DRAFT
                        ? _(msg`You can only detect fields in draft envelopes`)
                        : undefined
                    }
                  >
                    <SparklesIcon className="mr-2 -ml-1 h-4 w-4" />
                    <Trans>Detect with AI</Trans>
                  </Button>

                  <AiFieldDetectionDialog
                    open={isAiFieldDialogOpen}
                    onOpenChange={setIsAiFieldDialogOpen}
                    onComplete={onFieldDetectionComplete}
                    envelopeId={envelope.id}
                    teamId={envelope.teamId}
                  />

                  <AiFeaturesEnableDialog
                    open={isAiEnableDialogOpen}
                    onOpenChange={setIsAiEnableDialogOpen}
                    onEnabled={onAiFeaturesEnabled}
                  />
                </>
              )}
            </section>

            {/* Field details section. */}
            <AnimateGenericFadeInOut key={editorFields.selectedField?.formId}>
              {selectedField && (
                <section>
                  <Separator className="my-4" />

                  {searchParams.get('devmode') && <EnvelopeEditorFieldDiagnostics field={selectedField} />}

                  <div className="px-4 [&_label]:text-foreground/70 [&_label]:text-xs">
                    <h3 className="font-semibold text-sm">{_(FieldSettingsTypeTranslations[selectedField.type])}</h3>

                    {match(selectedField.type)
                      .with(FieldType.SIGNATURE, () => (
                        <EditorFieldSignatureForm
                          value={selectedField?.fieldMeta as TSignatureFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.CHECKBOX, () => (
                        <EditorFieldCheckboxForm
                          value={selectedField?.fieldMeta as TCheckboxFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.DATE, () => (
                        <EditorFieldDateForm
                          value={selectedField?.fieldMeta as TDateFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.DROPDOWN, () => (
                        <EditorFieldDropdownForm
                          value={selectedField?.fieldMeta as TDropdownFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.EMAIL, () => (
                        <EditorFieldEmailForm
                          value={selectedField?.fieldMeta as TEmailFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.INITIALS, () => (
                        <EditorFieldInitialsForm
                          value={selectedField?.fieldMeta as TInitialsFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.NAME, () => (
                        <EditorFieldNameForm
                          value={selectedField?.fieldMeta as TNameFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.NUMBER, () => (
                        <EditorFieldNumberForm
                          value={selectedField?.fieldMeta as TNumberFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.RADIO, () => (
                        <EditorFieldRadioForm
                          value={selectedField?.fieldMeta as TRadioFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .with(FieldType.TEXT, () => (
                        <EditorFieldTextForm
                          value={selectedField?.fieldMeta as TTextFieldMeta | undefined}
                          onValueChange={(value) => updateSelectedFieldMeta(value)}
                        />
                      ))
                      .otherwise(() => null)}
                  </div>
                </section>
              )}
            </AnimateGenericFadeInOut>
          </div>
        </EnvelopeEditorPanel>
      )}
    </div>
  );
};
