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

import { noop } from 'lodash';
import React, { useEffect, useState } from 'react';
import { Waveform } from '../../components/conversation/Waveform';
import type { ComputePeaksResult } from '../../components/VoiceNotesPlaybackContext';
import { VoiceNotesPlaybackContext } from '../../components/VoiceNotesPlaybackContext';
import * as log from '../../logging/log';

const BAR_COUNT = 47;

type Props = {
  // undefined if not downloaded yet
  audioUrl: string | undefined;
  computePeaks(url: string, barCount: number): Promise<ComputePeaksResult>;
  duration: number | undefined;
  onCorrupted: () => void;
  barMinHeight: number;
  barMaxHeight: number;
  currentTime: number;
};

function SmartWaveformImpl({
  audioUrl,
  barMinHeight,
  barMaxHeight,
  currentTime,
  duration: activeDuration,
  computePeaks,
  onCorrupted,
}: Props) {
  const [hasPeaks, setHasPeaks] = useState(false);
  const [peaks, setPeaks] = useState<ReadonlyArray<number>>(
    new Array(BAR_COUNT).fill(0)
  );

  const [duration, setDuration] = useState(activeDuration ?? 1e-23);

  const isComputing = audioUrl && !hasPeaks;

  // This effect loads audio file and computes its RMS peak for displaying the
  // waveform.
  useEffect(() => {
    if (!isComputing) {
      return noop;
    }

    log.info('MessageAudio: loading audio and computing waveform');

    let canceled = false;

    void (async () => {
      try {
        const { peaks: newPeaks, duration: newDuration } = await computePeaks(
          audioUrl,
          BAR_COUNT
        );
        if (canceled) {
          return;
        }
        setPeaks(newPeaks);
        setHasPeaks(true);
        setDuration(Math.max(newDuration, 1e-23));
      } catch (err) {
        log.error(
          'MessageAudio: computePeaks error, marking as corrupted',
          err
        );

        onCorrupted();
      }
    })();

    return () => {
      canceled = true;
    };
  }, [
    audioUrl,
    computePeaks,
    setDuration,
    setPeaks,
    setHasPeaks,
    onCorrupted,
    isComputing,
  ]);

  return (
    <Waveform
      peaks={peaks}
      barMinHeight={barMinHeight}
      barMaxHeight={barMaxHeight}
      duration={duration}
      currentTime={currentTime}
    />
  );
}

export function SmartWaveform(props: Omit<Props, 'computePeaks'>): JSX.Element {
  return (
    <VoiceNotesPlaybackContext.Consumer>
      {voiceNotesPlaybackProps => {
        return (
          voiceNotesPlaybackProps && (
            <SmartWaveformImpl
              {...props}
              computePeaks={voiceNotesPlaybackProps.computePeaks}
            />
          )
        );
      }}
    </VoiceNotesPlaybackContext.Consumer>
  );
}
