// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import { noop } from 'lodash';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useEscapeHandling } from '../hooks/useEscapeHandling';
import { usePrevious } from '../hooks/usePrevious';
import type { InMemoryAttachmentDraftType } from '../types/Attachment';
import { ErrorDialogAudioRecorderType } from '../types/AudioRecorder';
import type { LocalizerType } from '../types/Util';
import { DurationInSeconds, SECOND } from '../util/durations';
import { durationToPlaybackText } from '../util/durationToPlaybackText';
import { ConfirmationDialog } from './ConfirmationDialog';
import { RecordingComposer } from './RecordingComposer';
import { ToastVoiceNoteLimit } from './ToastVoiceNoteLimit';

export type Props = {
  i18n: LocalizerType;
  conversationId: string;
  onCancel: () => void;
  onSend: () => void;
  errorRecording: (e: ErrorDialogAudioRecorderType) => unknown;
  errorDialogAudioRecorderType?: ErrorDialogAudioRecorderType;
  addAttachment: (
    conversationId: string,
    attachment: InMemoryAttachmentDraftType
  ) => unknown;
  completeRecording: (
    conversationId: string,
    onRecordingComplete: (rec: InMemoryAttachmentDraftType) => unknown
  ) => unknown;
};

export function CompositionRecording({
  i18n,
  conversationId,
  onCancel,
  onSend,
  errorRecording,
  errorDialogAudioRecorderType,
  addAttachment,
  completeRecording,
}: Props): JSX.Element {
  useEscapeHandling(onCancel);

  const [showVoiceNoteLimitToast, setShowVoiceNoteLimitToast] = useState(true);

  // when interrupted (blur, switching convos)
  // stop recording and save draft
  const handleRecordingInterruption = useCallback(() => {
    completeRecording(conversationId, attachment => {
      addAttachment(conversationId, attachment);
    });
  }, [conversationId, completeRecording, addAttachment]);

  // switched to another app
  useEffect(() => {
    window.addEventListener('blur', handleRecordingInterruption);
    return () => {
      window.removeEventListener('blur', handleRecordingInterruption);
    };
  }, [handleRecordingInterruption]);

  // switched conversations
  const previousConversationId = usePrevious(conversationId, conversationId);
  useEffect(() => {
    if (previousConversationId !== conversationId) {
      handleRecordingInterruption();
    }
  });

  const handleCloseToast = useCallback(() => {
    setShowVoiceNoteLimitToast(false);
  }, []);

  useEffect(() => {
    return () => {
      handleCloseToast();
    };
  }, [handleCloseToast]);

  const startTime = useRef(Date.now());
  const [duration, setDuration] = useState(0);
  const drift = useRef(0);

  // update recording duration
  useEffect(() => {
    const timeoutId = setTimeout(() => {
      const now = Date.now();
      const newDurationMs = now - startTime.current;
      drift.current = newDurationMs % SECOND;
      setDuration(newDurationMs / SECOND);

      if (
        DurationInSeconds.fromMillis(newDurationMs) >= DurationInSeconds.HOUR
      ) {
        errorRecording(ErrorDialogAudioRecorderType.Timeout);
      }
    }, SECOND - drift.current);

    return () => {
      clearTimeout(timeoutId);
    };
  }, [duration, errorRecording]);

  let confirmationDialog: JSX.Element | undefined;
  if (errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.Timeout) {
    confirmationDialog = (
      <ConfirmationDialog
        dialogName="AudioCapture.sendAnyway"
        i18n={i18n}
        onCancel={onCancel}
        onClose={noop}
        cancelText={i18n('icu:discard')}
        actions={[
          {
            text: i18n('icu:sendAnyway'),
            style: 'affirmative',
            action: onSend,
          },
        ]}
      >
        {i18n('icu:voiceRecordingInterruptedMax')}
      </ConfirmationDialog>
    );
  } else if (
    errorDialogAudioRecorderType === ErrorDialogAudioRecorderType.ErrorRecording
  ) {
    confirmationDialog = (
      <ConfirmationDialog
        dialogName="AudioCapture.error"
        i18n={i18n}
        onCancel={onCancel}
        onClose={noop}
        cancelText={i18n('icu:ok')}
        actions={[]}
      >
        {i18n('icu:voiceNoteError')}
      </ConfirmationDialog>
    );
  }

  return (
    <RecordingComposer i18n={i18n} onCancel={onCancel} onSend={onSend}>
      <div className="CompositionRecording__microphone" />
      <div className="CompositionRecording__timer">
        {durationToPlaybackText(duration)}
      </div>

      {confirmationDialog}
      {showVoiceNoteLimitToast && (
        <ToastVoiceNoteLimit i18n={i18n} onClose={handleCloseToast} />
      )}
    </RecordingComposer>
  );
}