import type { EnvelopeEditorStep } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { useCurrentEnvelopeEditor } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { SpinnerBox } from '@documenso/ui/primitives/spinner';
import { msg } from '@lingui/core/macro';
import { EyeIcon, MousePointerIcon, UploadIcon } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useSearchParams } from 'react-router';
import { match } from 'ts-pattern';
import { EnvelopeEditorFieldsPage } from './envelope-editor-fields-page';
import EnvelopeEditorHeader from './envelope-editor-header';
import { EnvelopeEditorNavigation, type EnvelopeEditorStepData } from './envelope-editor-navigation';
import { EnvelopeEditorPanelProvider } from './envelope-editor-panel';
import { EnvelopeEditorPreviewPage } from './envelope-editor-preview-page';
import { EnvelopeEditorUploadPage } from './envelope-editor-upload-page';

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 { editorConfig, flushAutosave, resetForms } = useCurrentEnvelopeEditor();
  const [searchParams] = useSearchParams();
  const {
    general: { allowUploadAndRecipientStep, allowAddFieldsStep, allowPreviewStep },
  } = 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 (
    <EnvelopeEditorPanelProvider>
      <div className="flex h-dvh w-full min-w-0 flex-col overflow-hidden bg-envelope-editor-background">
        <EnvelopeEditorHeader />

        {/* Main Content Area */}
        <div className="flex min-h-0 min-w-0 flex-1 flex-col lg:flex-row">
          <EnvelopeEditorNavigation
            envelopeEditorSteps={envelopeEditorSteps}
            currentStepData={currentStepData}
            searchParamsStep={searchParamsStep}
          />

          {/* Main Content - Changes based on current step */}
          <div className="min-h-0 min-w-0 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>
    </EnvelopeEditorPanelProvider>
  );
};
