import type { EventInfo } from '../types';
import type { KycSubmission } from './kyc.types';
import { Dialog } from '@radix-ui/react-dialog';
import { useCallback, useEffect, useState } from 'react';
import { useStore } from 'zustand';
import { postKycQuery } from '@/client';
import { useCreateKycSession } from '@/hooks/data/useCreateKycSession';
import { useJoinEvent } from '@/hooks/data/useJoinEvent';
import { KycFailedDialogView } from './kyc-failed.dialog.view';
import { KycMethodSelectionDialogView } from './kyc-method-selection.dialog.view';
import { KycPendingDialogView } from './kyc-pending.dialog.view';
import { KycPromptDialogView } from './kyc-prompt.dialog.view';
import { KycSuccessDialogView } from './kyc-success.dialog.view';
import { createKycStore } from './kyc.state';

export function KycDialogContainer({ event, children }: { event: EventInfo; children: React.ReactNode }) {
  const [store] = useState(() => createKycStore(event.eventId));
  const isDialogOpen = useStore(store, s => s.isDialogOpen);
  const setIsDialogOpen = useStore(store, s => s.setIsDialogOpen);
  const stage = useStore(store, s => s.stage);
  const setStage = useStore(store, s => s.setStage);
  const setKycId = useStore(store, s => s.setKycId);

  const { mutateAsync: createKycSessionAsync } = useCreateKycSession();
  const { mutateAsync: joinEventAsync } = useJoinEvent();

  const joinEvent = useCallback(async (eventId: string, kycId: string, abortSignal?: AbortSignal) => {
    try {
      await joinEventAsync({
        signal: abortSignal,
        body: { event_id: eventId, kyc_id: kycId },
      });
      setStage('success');
    }
    catch (e) {
      console.error('Error joining event:', e);
      setStage('failed');
    }
  }, [joinEventAsync, setStage]);

  const onKycSessionCreate = useCallback(async (submission: KycSubmission) => {
    try {
      const { data } = await createKycSessionAsync(submission);
      setKycId(data!.kyc_id!);
      if (data!.status === 'success') {
        await joinEvent(event.eventId, data!.kyc_id!, undefined);
      }
      else if (data!.status === 'processing') {
        window.open(data!.redirect_uri, '_blank');
        setStage('pending');
      }
    }
    catch (e) {
      console.error(e);
      setStage('failed');
    }
  }, [event.eventId, joinEvent, createKycSessionAsync, setKycId, setStage]);

  useEffect(() => {
    if (stage !== 'pending' || !isDialogOpen) {
      return;
    }

    const controller = new AbortController();
    let timer: NodeJS.Timeout;

    const poll = async () => {
      try {
        const { data } = await postKycQuery({
          signal: controller.signal,
          body: { kyc_id: store.getState().kycId! },
        });

        const status = data?.data?.status;

        if (status === 'success') {
          void joinEvent(event.eventId, store.getState().kycId!, controller.signal);
        }
        else if (status === 'failed') {
          setStage('failed');
        }
        else if (status === 'pending') {
          timer = setTimeout(() => void poll(), 1000);
        }
        else {
          // What the fuck?
          setStage('failed');
        }
      }
      catch (e) {
        if ((e as Error).name === 'AbortError')
          return;
        console.error('Error fetching KYC status:', e);
        setStage('failed');
      }
    };

    void poll();

    return () => {
      controller.abort();
      clearTimeout(timer);
    };
  }, [stage, store, setStage, isDialogOpen, joinEvent, event.eventId]);

  return (
    <Dialog
      open={isDialogOpen}
      onOpenChange={setIsDialogOpen}
    >
      {children}
      {stage === 'prompt' && <KycPromptDialogView next={() => setStage('methodSelection')} />}
      {stage === 'methodSelection' && <KycMethodSelectionDialogView onSubmit={onKycSessionCreate} />}
      {stage === 'pending' && <KycPendingDialogView />}
      {stage === 'success' && <KycSuccessDialogView />}
      {stage === 'failed' && <KycFailedDialogView />}
    </Dialog>
  );
}
