import * as React from 'react';
import {augmentAudioFile, getMimeTypeDescription} from 'packages/web-kitchensink-reactnext/src/utils/blob';
import {
  formatFileSize,
  formatNumeral,
  formatSecondsDurationConcise,
  formatSecondsDurationPrecise,
} from 'packages/web-kitchensink-reactnext/src/utils/numeral';
import theme from 'packages/web-kitchensink-reactnext/src/styles/theme';
import {useMediaControls} from '../../hooks/interactive';
import {useFileMetadata, useFileUrl} from 'src/index';

import clsx from 'clsx';
import {SpectrogramCanvas, WaveformCanvas} from 'packages/web-kitchensink-reactnext/src/packages/react-wavesurfer';
import {Slider} from 'categories/number/react';
import {KeyValueTable} from 'categories/information/react';
import {useClientSide} from 'packages/react-utils';
import type {CommonPreviewProps} from '../../../../../categories/blob/react/src/components/FileSelectBox';

export type AudioFilePreviewDerivedElement = HTMLAudioElement;

export interface AudioFilePreviewProps<F extends Partial<File> = Partial<File>>
  extends Omit<React.HTMLProps<AudioFilePreviewDerivedElement>, 'controls'>, CommonPreviewProps<F> {}

export const AudioFilePreview = React.forwardRef<AudioFilePreviewDerivedElement, AudioFilePreviewProps>(({
  file,
  style,
  className,
  enhanced: enhancedProp = false,
  disabled = false,
  ...etcProps
}, forwardedRef) => {
  const { fileWithUrl } = useFileUrl({
    file,
  });
  const { fileWithMetadata, error } = useFileMetadata({
    file: fileWithUrl,
    augmentFunction: augmentAudioFile,
  });
  const {
    mediaControllerRef,
    refreshControls,
    reset,
    updateSeekFromPlayback,
    isPlaying,
    isSeeking,
    currentTimeDisplay = 0,
    seekTimeDisplay = 0,
    durationDisplay = 0,
    isSeekTimeCountingDown,
    adjustVolume,
    volumeRef,
    handleAction,
    filenameRef,
    seekRef,
    startSeek,
    endSeek,
    setSeek,
    visualizationId,
    formId,
  } = useMediaControls<HTMLAudioElement>({
    controllerRef: forwardedRef,
    visualizationMode: 'waveform',
  });
  const { clientSide } = useClientSide({ clientSide: enhancedProp });

  if (!fileWithMetadata) {
    return null;
  }

  const finalSeekTimeDisplay = isSeekTimeCountingDown ? (durationDisplay - seekTimeDisplay) : seekTimeDisplay;
  const finalCurrentTimeDisplay = isSeekTimeCountingDown ? (durationDisplay - currentTimeDisplay) : currentTimeDisplay;

  return (
    <div
      className={clsx(
        'flex flex-col sm:grid sm:grid-cols-3 gap-8 w-full',
        className,
      )}
      style={style}
    >
      <div className="sm:h-full relative col-span-2">
        {
          typeof fileWithMetadata.url === 'string'
          && (
            <div
              className="w-full h-full bg-black flex flex-col items-stretch"
              key={`${fileWithMetadata.url}:${fileWithMetadata.type}`}
            >
              <div
                className="w-full flex-auto relative aspect-video sm:aspect-auto"
              >
                <audio
                  {...etcProps}
                  controls={!clientSide}
                  ref={mediaControllerRef}
                  onLoadedMetadata={refreshControls}
                  onDurationChange={refreshControls}
                  onEnded={reset}
                  onTimeUpdate={updateSeekFromPlayback}
                  data-testid="preview"
                >
                  <source
                    src={fileWithMetadata.url}
                    type={fileWithMetadata.type}
                  />
                  Audio playback not supported.
                </audio>
                {clientSide && (
                  <>
                    <div className="flex justify-end w-full h-full gap-4 absolute top-0 right-0 z-[5] px-4">
                      <div className="contents">
                        <input
                          type="radio"
                          name="visualizationMode"
                          value="waveform"
                          className="sr-only peer/waveform"
                          defaultChecked
                          id={`${visualizationId}-waveform`}
                        />
                        <label
                          htmlFor={`${visualizationId}-waveform`}
                          className={clsx(
                            'relative z-[5]',
                            'h-12 flex items-center justify-center leading-none gap-4 select-none',
                            'text-primary cursor-pointer',
                            'peer-focus/waveform:text-secondary',
                            'peer-active/waveform:text-tertiary',
                            'peer-checked/waveform:text-tertiary',
                            'peer-disabled/waveform:text-primary peer-disabled/waveform:cursor-not-allowed peer-disabled/waveform:opacity-50',
                          )}
                        >
                          <span
                            className={clsx(
                              'flex items-center uppercase font-bold h-full w-full whitespace-nowrap overflow-hidden text-ellipsis',
                            )}
                          >
                            Waveform
                          </span>
                        </label>
                        <WaveformCanvas
                          className={clsx(
                            'absolute w-full sm:h-full top-0 left-0 block object-center object-contain flex-auto aspect-video sm:aspect-auto bg-primary/10 cursor-text opacity-0',
                            'peer-checked/waveform:opacity-100',
                          )}
                          audioRef={mediaControllerRef}
                          data-testid="preview"
                          barWidth={1}
                          barGap={1}
                          progressColor={`rgb(${theme['color-primary']})`}
                          waveColor={`rgb(${theme['color-primary'].split(' ').map((c) => Math.floor(Number(c) / 2)).join(' ')})`}
                          interact
                          // waveColor={`rgb(${theme.primary})`}
                          // barHeight={4}
                          // minPxPerSec={20000}
                          // hideScrollbar
                          // autoCenter
                          // autoScroll
                        />
                      </div>
                      <div
                        className="contents"
                      >
                        <input
                          type="radio"
                          name="visualizationMode"
                          value="spectrum"
                          className="sr-only peer/waveform"
                          id={`${visualizationId}-spectrum`}
                        />
                        <label
                          htmlFor={`${visualizationId}-spectrum`}
                          className={clsx(
                            'relative z-[5]',
                            'h-12 flex items-center justify-center leading-none gap-4 select-none',
                            'text-primary cursor-pointer',
                            'peer-focus/waveform:text-secondary',
                            'peer-active/waveform:text-tertiary',
                            'peer-checked/waveform:text-tertiary',
                            'peer-disabled/waveform:text-primary peer-disabled/waveform:cursor-not-allowed peer-disabled/waveform:opacity-50',
                          )}
                        >
                          <span
                            className={clsx(
                              'flex items-center uppercase font-bold h-full w-full whitespace-nowrap overflow-hidden text-ellipsis',
                            )}
                          >
                            Spectrum
                          </span>
                        </label>
                        <SpectrogramCanvas
                          className={clsx(
                            'absolute w-full sm:h-full top-0 left-0 block object-center object-contain flex-auto aspect-video sm:aspect-auto bg-primary/10 pointer-events-none opacity-0',
                            'peer-checked/waveform:opacity-100',
                          )}
                          audioRef={mediaControllerRef}
                          data-testid="preview"
                          barWidth={1}
                          barGap={1}
                          waveColor={`rgb(${theme['color-primary']})`}
                          cursorWidth={2}
                          minPxPerSec={20000}
                          hideScrollbar
                          autoCenter
                          autoScroll
                        />
                      </div>
                    </div>
                  </>
                )}
              </div>
              {clientSide && (
                <div className="w-full flex-shrink-0 h-10 flex gap-4 items-center bg-[#000000] px-3">
                  <div
                    className="py-1 w-14 h-full flex-shrink-0 text-primary flex items-center justify-center"
                  >
                    <button
                      className={
                        clsx(
                          'w-full h-full flex-shrink-0 text-primary flex items-center justify-center bg-primary/30 rounded',
                          'focus:text-secondary focus:outline-0 focus:bg-secondary/30',
                          'active:text-tertiary active:bg-tertiary/30',
                          'disabled:text-primary disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-primary/30',
                        )
                      }
                      type="submit"
                      name="action"
                      value="togglePlayback"
                      form={formId}
                    >
                      {
                        isPlaying
                          ? (
                            <svg
                              aria-label="Pause"
                              viewBox="0 0 24 24"
                              className="w-6 h-6 fill-none stroke-current stroke-2 linecap-round linejoin-round"
                            >
                              <rect
                                x="6"
                                y="4"
                                width="4"
                                height="16"
                              />
                              <rect
                                x="14"
                                y="4"
                                width="4"
                                height="16"
                              />
                            </svg>
                          )
                          : (
                            <svg
                              aria-label="Play"
                              viewBox="0 0 24 24"
                              className="w-6 h-6 fill-none stroke-current stroke-2 linecap-round linejoin-round"
                            >
                              <polygon points="5 3 19 12 5 21 5 3" />
                            </svg>
                          )
                      }
                    </button>
                  </div>
                  <div className="flex-auto w-full flex items-center gap-2 text-sm relative">
                    <button
                      className="absolute overflow-hidden w-12 opacity-0 h-10 peer/seek"
                      title="Toggle Seek Time Count Mode"
                      type="submit"
                      name="action"
                      value="toggleSeekTimeCountMode"
                      form={formId}
                    >
                      Toggle Seek Time Count Mode
                    </button>
                    <div
                      className={clsx(
                        'before:block before:content-[attr(title)] contents tabular-nums flex-auto text-primary font-bold',
                        'peer-focus/seek:text-secondary peer-focus/seek:outline-0',
                        'peer-active/seek:text-tertiary',
                        'peer-disabled/seek:text-primary \'peer-disabled/seek:cursor-not-allowed \'peer-disabled/seek:opacity-50'
                      )}
                      title={
                        `${
                          isSeekTimeCountingDown ? '−' : '+'
                        }${
                          isSeeking
                            ? formatSecondsDurationConcise(finalSeekTimeDisplay)
                            : formatSecondsDurationConcise(finalCurrentTimeDisplay)
                        }`
                      }
                    >
                      <Slider
                        className="text-base flex-auto"
                        ref={seekRef}
                        min={0}
                        max={durationDisplay}
                        onMouseDown={startSeek}
                        onMouseUp={endSeek}
                        onChange={setSeek}
                        defaultValue="0"
                        step="any"
                        length="100%"
                      />
                    </div>
                    <span className="tabular-nums font-bold">
                      {formatSecondsDurationConcise(durationDisplay)}
                    </span>
                  </div>
                  <div
                    className="flex-shrink-0 w-12 flex items-center"
                  >
                    <Slider
                      className="flex-auto"
                      ref={volumeRef}
                      max={1}
                      min={0}
                      onChange={adjustVolume}
                      step="any"
                      defaultValue="1"
                      title="Volume"
                      length="100%"
                    />
                  </div>
                </div>
              )}
            </div>
          )
        }
      </div>
      <div className="flex-shrink-0 m-0 flex flex-col gap-4 justify-between">
        <KeyValueTable
          hiddenKeys
          data-testid="infoBox"
          properties={[
            Boolean(fileWithMetadata.name) && {
              key: 'Name',
              className: 'font-bold',
              valueProps: {
                ref: filenameRef,
                title: fileWithMetadata.name,
                children: fileWithMetadata.name,
              },
            },
            (clientSide && Boolean(getMimeTypeDescription(fileWithMetadata.type, fileWithMetadata.name) || '(Loading)') || Boolean(fileWithMetadata.type)) && {
              key: 'Type',
              valueProps: {
                className: clsx(
                  !getMimeTypeDescription(fileWithMetadata.type, fileWithMetadata.name) && 'opacity-50'
                ),
                children: getMimeTypeDescription(fileWithMetadata.type, fileWithMetadata.name) || '(Loading)',
              },
            },
            (clientSide && Boolean(formatFileSize(fileWithMetadata.size) || '(Loading)') || Boolean(fileWithMetadata.size)) && {
              key: 'Size',
              valueProps: {
                className: clsx(
                  !formatFileSize(fileWithMetadata.size) && 'opacity-50'
                ),
                title: `${formatNumeral(fileWithMetadata.size ?? 0)} byte(s)`,
                children: formatFileSize(fileWithMetadata.size) || '(Loading)',
              },
            },
            typeof fileWithMetadata.metadata?.duration === 'number'
            && {
              key: 'Duration',
              valueProps: {
                className: clsx(
                  !formatSecondsDurationPrecise(fileWithMetadata.metadata.duration) && 'opacity-50'
                ),
                title: `${formatNumeral(fileWithMetadata.metadata.duration ?? 0)} second(s)`,
                children: formatSecondsDurationPrecise(fileWithMetadata.metadata.duration),
              },
            },
          ]}
        />
        {clientSide && (
          <form
            id={formId}
            onSubmit={handleAction}
            className="flex gap-4 justify-end"
          >
            <fieldset
              disabled={disabled || typeof error !== 'undefined'}
              className="contents"
            >
              <legend className="sr-only">
                Controls
              </legend>
              <button
                type="submit"
                name="action"
                value="download"
                className={clsx(
                  'h-12 flex bg-negative text-primary disabled:text-primary focus:text-secondary active:text-tertiary items-center justify-center leading-none gap-4 select-none',
                  'focus:outline-0',
                  'disabled:opacity-50 disabled:cursor-not-allowed',
                )}
              >
              <span
                className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded"
              >
                Download
              </span>
              </button>
            </fieldset>
          </form>
        )}
      </div>
    </div>
  );
});

AudioFilePreview.displayName = 'AudioFilePreview';
