import { useRef, useState } from 'react';
import { toError } from '@theorchard/songwhip-utils';
import mitt from 'mitt';

import type { CreateFlowEmitter, CreateFlowStageDefinition } from './types';

import Box from '~/src/components/Box';
import { DialogBoxHeader } from '~/src/components/DialogBox/DialogBoxHeader';
import { useAppAlert } from '~/src/components/NextApp/lib/CoreUi';
import PageLoading from '~/src/components/PageLoading';
import { useListenOnEvent } from '~/src/hooks/useListenOnEvent';
import { useI18n } from '~/src/lib/i18n';
import { useAppRouter } from '~/src/lib/router2';
import { reportError } from '~/src/lib/sentry';
import { NextButton } from '../NextButton';

export interface CreateFlowProps {
  hideNextButton?: boolean;
  stages: CreateFlowStageDefinition[];
  onSubmit: (data: Record<string, unknown>) => Promise<void>;
  onStageComplete?: (params: {
    stageId: string;
    data: Record<string, unknown>;
    events: CreateFlowEmitter;
  }) => void;
}

/**
 * CreateFlow component that manages the flow of stages in a page creation process.
 * It takes an array of stages and renders the active stage based on stage validity.
 */
export function CreateFlow({
  stages,
  onSubmit,
  onStageComplete,
  hideNextButton,
}: CreateFlowProps) {
  const router = useAppRouter();
  const query = router.getQuery();
  const emitter = useRef<CreateFlowEmitter>(mitt());
  const [isSubmitting, setIsSubmitting] = useState(false);
  const { t } = useI18n('create');
  const appAlert = useAppAlert();

  const activeStageId = query.stage;

  const activeStage =
    stages.find((stage) => stage.id == activeStageId) ?? stages[0];

  const activeStageIndex = stages.findIndex((s) => s.id === activeStage.id);
  const prevStage = stages[activeStageIndex - 1];
  const nextStage = stages[activeStageIndex + 1];

  const updateQuery = async (data: Record<string, unknown> = {}) => {
    const serializedData = new URLSearchParams();

    Object.entries(data).forEach(([key, value]) => {
      if (value !== undefined) {
        serializedData.append(key, String(value));
      }
    });

    const params = Object.fromEntries(serializedData.entries());
    await router.setQuery(params, { reset: true });
  };

  useListenOnEvent(emitter.current, 'stageComplete', async (data) => {
    if (isSubmitting) return;

    await updateQuery({ ...data, stage: nextStage?.id ?? activeStage.id });

    onStageComplete?.({
      stageId: activeStageId,
      data,
      events: emitter.current,
    });
  });

  useListenOnEvent(emitter.current, 'submit', async (data) => {
    if (isSubmitting) return;

    setIsSubmitting(true);

    const params = { ...router.getQuery(), ...data };
    await updateQuery(params);

    try {
      await onSubmit(params);
    } catch (error) {
      appAlert({
        title: t('errorCreatingPage'),
        content: toError(error).message,
      });

      reportError({ error });
    } finally {
      setIsSubmitting(false);
    }
  });

  if (isSubmitting) return <PageLoading text={t('creatingPage')} />;

  return (
    <Box coverParent zIndex={1}>
      {activeStage.HeaderComponent ? (
        <activeStage.HeaderComponent
          events={emitter.current}
          definition={activeStage}
        />
      ) : (
        <DialogBoxHeader
          title={activeStage.title}
          background="none"
          zIndex={1}
          onBackClick={async () => {
            if (prevStage) {
              await router.setQuery(
                { ...query, stage: prevStage.id },
                { reset: true }
              );
            } else {
              await router.push('/create');
            }
          }}
          renderRight={
            hideNextButton || activeStage.hideNextButton
              ? undefined
              : () => (
                  <NextButton
                    testId="nextButton"
                    onClick={() => emitter.current.emit('nextButtonPressed')}
                  />
                )
          }
        />
      )}
      <Box padding="1rem 2rem 2rem" centerContent>
        <activeStage.Component
          events={emitter.current}
          definition={activeStage}
        />
      </Box>
    </Box>
  );
}
