import type { EnvelopeEditorStep } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Separator } from '@documenso/ui/primitives/separator';
import { SpinnerBox } from '@documenso/ui/primitives/spinner';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { motion } from 'framer-motion';
import {
  ArrowLeftIcon,
  CopyPlusIcon,
  DownloadCloudIcon,
  EyeIcon,
  FileOutputIcon,
  LinkIcon,
  type LucideIcon,
  MousePointerIcon,
  SendIcon,
  SettingsIcon,
  Trash2Icon,
  UploadIcon,
} from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Link, useNavigate, useSearchParams } from 'react-router';
import { match } from 'ts-pattern';

import { EnvelopeDeleteDialog } from '~/components/dialogs/envelope-delete-dialog';
import { EnvelopeDistributeDialog } from '~/components/dialogs/envelope-distribute-dialog';
import { EnvelopeDownloadDialog } from '~/components/dialogs/envelope-download-dialog';
import { EnvelopeDuplicateDialog } from '~/components/dialogs/envelope-duplicate-dialog';
import { EnvelopeRedistributeDialog } from '~/components/dialogs/envelope-redistribute-dialog';
import { EnvelopeSaveAsTemplateDialog } from '~/components/dialogs/envelope-save-as-template-dialog';
import { TemplateDirectLinkDialog } from '~/components/dialogs/template-direct-link-dialog';
import { EnvelopeEditorSettingsDialog } from '~/components/general/envelope-editor/envelope-editor-settings-dialog';

import { EnvelopeEditorFieldsPage } from './envelope-editor-fields-page';
import EnvelopeEditorHeader from './envelope-editor-header';
import { EnvelopeEditorPreviewPage } from './envelope-editor-preview-page';
import { EnvelopeEditorUploadPage } from './envelope-editor-upload-page';

type EnvelopeEditorStepData = {
  id: string;
  title: MessageDescriptor;
  icon: LucideIcon;
  description: MessageDescriptor;
};

const UPLOAD_STEP = {
  id: 'upload',
  title: msg`Document & Recipients`,
  icon: UploadIcon,
  description: msg`Upload documents and add recipients`,
};

const ADD_FIELDS_STEP = {
  id: 'addFields',
  title: msg`Add Fields`,
  icon: MousePointerIcon,
  description: msg`Place and configure form fields in the document`,
};

const PREVIEW_STEP = {
  id: 'preview',
  title: msg`Preview`,
  icon: EyeIcon,
  description: msg`Preview the document before sending`,
};

export const EnvelopeEditor = () => {
  const { t } = useLingui();

  const navigate = useNavigate();

  const {
    envelope,
    editorConfig,
    isDocument,
    isTemplate,
    relativePath,
    navigateToStep,
    syncEnvelope,
    flushAutosave,
    resetForms,
  } = useCurrentEnvelopeEditor();

  const [searchParams, setSearchParams] = useSearchParams();

  const {
    general: { minimizeLeftSidebar, allowUploadAndRecipientStep, allowAddFieldsStep, allowPreviewStep },
    actions: {
      allowDistributing,
      allowDirectLink,
      allowDuplication,
      allowSaveAsTemplate,
      allowDownloadPDF,
      allowDeletion,
    },
  } = editorConfig;

  const envelopeEditorSteps = useMemo(() => {
    const steps: EnvelopeEditorStepData[] = [];

    if (allowUploadAndRecipientStep) {
      steps.push(UPLOAD_STEP);
    }

    if (allowAddFieldsStep) {
      steps.push(ADD_FIELDS_STEP);
    }

    if (allowPreviewStep) {
      steps.push(PREVIEW_STEP);
    }

    return steps.map((step, index) => ({
      ...step,
      order: index + 1,
    }));
  }, [editorConfig]);

  const searchParamsStep = useMemo(() => {
    // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
    const searchParamStep = searchParams.get('step') as EnvelopeEditorStep | undefined;

    // Empty URL param equals upload, otherwise use the step URL param
    if (!searchParamStep) {
      return 'upload';
    }

    const validSteps: EnvelopeEditorStep[] = ['upload', 'addFields', 'preview'];

    if (validSteps.includes(searchParamStep)) {
      return searchParamStep;
    }

    return 'upload';
  }, [searchParams]);

  const [pageToRender, setPageToRender] = useState<EnvelopeEditorStep | 'loading'>(searchParamsStep);

  const latestStepChangeTime = useRef(0);

  const handleStepChange = async (step: EnvelopeEditorStep) => {
    setPageToRender('loading');

    const currentTime = Date.now();
    latestStepChangeTime.current = currentTime;

    await flushAutosave().then(() => {
      if (currentTime !== latestStepChangeTime.current) {
        return;
      }

      resetForms();
      setPageToRender(step);
    });
  };

  // Watch the URL params and setStep if the step changes.
  useEffect(() => {
    const stepParam = searchParams.get('step') || envelopeEditorSteps[0].id;

    const foundStep = envelopeEditorSteps.find((step) => step.id === stepParam);

    if (foundStep && foundStep.id !== pageToRender) {
      // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
      void handleStepChange(foundStep.id as EnvelopeEditorStep);
    }
  }, [searchParams]);

  const currentStepData = envelopeEditorSteps.find((step) => step.id === searchParamsStep) || envelopeEditorSteps[0];

  return (
    <div className="h-screen w-screen bg-envelope-editor-background">
      <EnvelopeEditorHeader />

      {/* Main Content Area */}
      <div className="flex h-[calc(100vh-4rem)] w-screen">
        {/* Left Section - Step Navigation */}
        <div
          className={cn('flex w-80 flex-shrink-0 flex-col overflow-y-auto border-border border-r bg-background py-4', {
            'w-14': minimizeLeftSidebar,
          })}
        >
          {/* Left section step selector. */}
          {minimizeLeftSidebar ? (
            <div className="flex justify-center px-4">
              <div className="relative flex h-10 w-10 items-center justify-center">
                <svg className="size-10 -rotate-90" viewBox="0 0 40 40" aria-hidden>
                  {/* Track circle */}
                  <circle
                    cx="20"
                    cy="20"
                    r="16"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="3"
                    className="text-muted"
                  />
                  {/* Progress arc */}
                  <motion.circle
                    cx="20"
                    cy="20"
                    r="16"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="3"
                    strokeLinecap="round"
                    className="text-primary"
                    strokeDasharray={2 * Math.PI * 16}
                    initial={false}
                    animate={{
                      strokeDashoffset:
                        2 * Math.PI * 16 * (1 - (currentStepData.order ?? 0) / envelopeEditorSteps.length),
                    }}
                    transition={{ type: 'spring', stiffness: 300, damping: 30 }}
                  />
                </svg>
                <span className="absolute inset-0 flex items-center justify-center font-semibold text-[10px] text-foreground">
                  <Trans context="The step counter">
                    {currentStepData.order}/{envelopeEditorSteps.length}
                  </Trans>
                </span>
              </div>
            </div>
          ) : (
            <div className="px-4">
              <h3 className="flex items-end justify-between font-semibold text-foreground text-sm">
                {isDocument ? <Trans>Document Editor</Trans> : <Trans>Template Editor</Trans>}

                <span className="ml-2 rounded border bg-muted/50 px-2 py-0.5 text-muted-foreground text-xs">
                  <Trans context="The step counter">
                    Step {currentStepData.order}/{envelopeEditorSteps.length}
                  </Trans>
                </span>
              </h3>

              <div className="relative my-4 h-[4px] rounded-md bg-muted">
                <motion.div
                  layout="size"
                  layoutId="document-flow-container-step"
                  className="absolute inset-y-0 left-0 bg-primary"
                  style={{
                    width: `${(100 / envelopeEditorSteps.length) * (currentStepData.order ?? 0)}%`,
                  }}
                />
              </div>
            </div>
          )}

          <div
            className={cn('space-y-3', {
              'px-4': !minimizeLeftSidebar,
              'mt-4 flex flex-col items-center': minimizeLeftSidebar,
            })}
          >
            {envelopeEditorSteps.map((step) => {
              const Icon = step.icon;
              const isActive = searchParamsStep === step.id;

              return (
                <button
                  key={step.id}
                  data-testid={`envelope-editor-step-${step.id}`}
                  type="button"
                  className={cn(
                    `cursor-pointer rounded-lg text-left transition-colors ${
                      isActive
                        ? 'border border-green-200 bg-green-50 dark:border-green-500/20 dark:bg-green-500/10'
                        : 'border border-gray-200 hover:bg-gray-50 dark:border-gray-400/20 dark:hover:bg-gray-400/10'
                    }`,
                    {
                      'p-3': !minimizeLeftSidebar,
                    },
                  )}
                  onClick={() => void navigateToStep(step.id as EnvelopeEditorStep)}
                >
                  <div className="flex items-center space-x-3">
                    <div
                      className={`rounded border p-2 ${
                        isActive
                          ? 'border-green-200 bg-green-50 dark:border-green-500/20 dark:bg-green-500/10'
                          : 'border-gray-100 bg-gray-100 dark:border-gray-400/20 dark:bg-gray-400/10'
                      }`}
                    >
                      <Icon className={`h-4 w-4 ${isActive ? 'text-green-600' : 'text-gray-600'}`} />
                    </div>

                    {!minimizeLeftSidebar && (
                      <div>
                        <div
                          className={`font-medium text-sm ${
                            isActive
                              ? 'text-green-900 dark:text-green-400'
                              : 'text-foreground dark:text-muted-foreground'
                          }`}
                        >
                          {t(step.title)}
                        </div>
                        <div className="text-muted-foreground text-xs">{t(step.description)}</div>
                      </div>
                    )}
                  </div>
                </button>
              );
            })}
          </div>

          <Separator
            className={cn('my-6', {
              'mx-auto mb-4 w-4/5': minimizeLeftSidebar,
            })}
          />

          {/* Quick Actions. */}
          <div
            className={cn('space-y-3 px-4 [&_.lucide]:text-muted-foreground', {
              'px-2': minimizeLeftSidebar,
            })}
          >
            {!minimizeLeftSidebar && (
              <h4 className="font-semibold text-foreground text-sm">
                <Trans>Quick Actions</Trans>
              </h4>
            )}

            {editorConfig.settings && (
              <EnvelopeEditorSettingsDialog
                trigger={
                  <Button variant="ghost" size="sm" className="w-full justify-start" title={t(msg`Settings`)}>
                    <SettingsIcon className="h-4 w-4" />

                    {!minimizeLeftSidebar && (
                      <span className="ml-2">
                        {isDocument ? <Trans>Document Settings</Trans> : <Trans>Template Settings</Trans>}
                      </span>
                    )}
                  </Button>
                }
              />
            )}

            {isDocument && allowDistributing && (
              <>
                <EnvelopeDistributeDialog
                  documentRootPath={relativePath.documentRootPath}
                  trigger={
                    <Button variant="ghost" size="sm" className="w-full justify-start" title={t(msg`Send Envelope`)}>
                      <SendIcon className="h-4 w-4" />

                      {!minimizeLeftSidebar && (
                        <span className="ml-2">
                          <Trans>Send Document</Trans>
                        </span>
                      )}
                    </Button>
                  }
                />

                <EnvelopeRedistributeDialog
                  envelope={envelope}
                  trigger={
                    <Button variant="ghost" size="sm" className="w-full justify-start" title={t(msg`Resend Envelope`)}>
                      <SendIcon className="h-4 w-4" />

                      {!minimizeLeftSidebar && (
                        <span className="ml-2">
                          <Trans>Resend Document</Trans>
                        </span>
                      )}
                    </Button>
                  }
                />
              </>
            )}

            {isTemplate && allowDirectLink && (
              <TemplateDirectLinkDialog
                templateId={mapSecondaryIdToTemplateId(envelope.secondaryId)}
                directLink={envelope.directLink}
                recipients={envelope.recipients}
                onCreateSuccess={async () => await syncEnvelope()}
                onDeleteSuccess={async () => await syncEnvelope()}
                trigger={
                  <Button variant="ghost" size="sm" className="w-full justify-start" title={t(msg`Direct Link`)}>
                    <LinkIcon className="h-4 w-4" />

                    {!minimizeLeftSidebar && (
                      <span className="ml-2">
                        <Trans>Direct Link</Trans>
                      </span>
                    )}
                  </Button>
                }
              />
            )}

            {allowDuplication && (
              <EnvelopeDuplicateDialog
                envelopeId={envelope.id}
                envelopeType={envelope.type}
                trigger={
                  <Button variant="ghost" size="sm" className="w-full justify-start" title={t(msg`Duplicate Envelope`)}>
                    <CopyPlusIcon className="h-4 w-4" />

                    {!minimizeLeftSidebar && (
                      <span className="ml-2">
                        {isDocument ? <Trans>Duplicate Document</Trans> : <Trans>Duplicate Template</Trans>}
                      </span>
                    )}
                  </Button>
                }
              />
            )}

            {allowSaveAsTemplate && isDocument && (
              <EnvelopeSaveAsTemplateDialog
                envelopeId={envelope.id}
                trigger={
                  <Button variant="ghost" size="sm" className="w-full justify-start" title={t(msg`Save as Template`)}>
                    <FileOutputIcon className="h-4 w-4" />

                    {!minimizeLeftSidebar && (
                      <span className="ml-2">
                        <Trans>Save as Template</Trans>
                      </span>
                    )}
                  </Button>
                }
              />
            )}

            {allowDownloadPDF && (
              <EnvelopeDownloadDialog
                envelopeId={envelope.id}
                envelopeStatus={envelope.status}
                isLegacy={envelope.internalVersion === 1}
                envelopeItems={envelope.envelopeItems}
                trigger={
                  <Button variant="ghost" size="sm" className="w-full justify-start" title={t(msg`Download PDF`)}>
                    <DownloadCloudIcon className="h-4 w-4" />

                    {!minimizeLeftSidebar && (
                      <span className="ml-2">
                        <Trans>Download PDF</Trans>
                      </span>
                    )}
                  </Button>
                }
              />
            )}

            {/* Check envelope ID since it can be in embedded create mode. */}
            {allowDeletion && envelope.id && (
              <EnvelopeDeleteDialog
                id={envelope.id}
                type={envelope.type}
                status={envelope.status}
                title={envelope.title}
                canManageDocument={true}
                trigger={
                  <Button
                    type="button"
                    variant="ghost"
                    size="sm"
                    className="w-full justify-start"
                    title={t(msg`Delete Envelope`)}
                  >
                    <Trash2Icon className="h-4 w-4" />

                    {!minimizeLeftSidebar && (
                      <span className="ml-2">
                        {isDocument ? <Trans>Delete Document</Trans> : <Trans>Delete Template</Trans>}
                      </span>
                    )}
                  </Button>
                }
                onDelete={async () => {
                  await navigate(
                    envelope.type === EnvelopeType.DOCUMENT
                      ? relativePath.documentRootPath
                      : relativePath.templateRootPath,
                  );
                }}
              />
            )}
          </div>

          {/* Footer of left sidebar. */}
          {!editorConfig.embedded && (
            <div
              className={cn('mt-auto px-4', {
                'px-2': minimizeLeftSidebar,
              })}
            >
              <Button
                variant="ghost"
                className={cn('w-full justify-start', {
                  'flex items-center justify-center': minimizeLeftSidebar,
                })}
                asChild
              >
                <Link to={relativePath.basePath}>
                  <ArrowLeftIcon className="h-4 w-4 flex-shrink-0" />

                  {!minimizeLeftSidebar && (
                    <span className="ml-2">
                      {isDocument ? <Trans>Return to documents</Trans> : <Trans>Return to templates</Trans>}
                    </span>
                  )}
                </Link>
              </Button>
            </div>
          )}
        </div>

        {/* Main Content - Changes based on current step */}
        <div className="flex-1 overflow-y-auto">
          {match({
            pageToRender,
            allowUploadAndRecipientStep,
            allowAddFieldsStep,
            allowPreviewStep,
          })
            .with({ pageToRender: 'loading' }, () => <SpinnerBox className="py-32" />)
            .with({ pageToRender: 'upload', allowUploadAndRecipientStep: true }, () => <EnvelopeEditorUploadPage />)
            .with({ pageToRender: 'addFields', allowAddFieldsStep: true }, () => <EnvelopeEditorFieldsPage />)
            .with({ pageToRender: 'preview', allowPreviewStep: true }, () => <EnvelopeEditorPreviewPage />)
            .otherwise(() => null)}
        </div>
      </div>
    </div>
  );
};
