import * as React from 'react';
import {augmentVideoFile, getMimeTypeDescription} from 'packages/web-kitchensink-reactnext/src/utils/blob';
import {formatFileSize, formatNumeral, formatSecondsDurationConcise} from 'packages/web-kitchensink-reactnext/src/utils/numeral';
import {useFileMetadata, useFileUrl, useMediaControls} from 'src/index';
import clsx from 'clsx';
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 VideoFilePreviewDerivedComponent = HTMLVideoElement;

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

export const VideoFilePreview = React.forwardRef<VideoFilePreviewDerivedComponent, VideoFilePreviewProps>(({
  file,
  className,
  style,
  disabled = false,
  enhanced: enhancedProp = false,
  ...etcProps
}, forwardedRef) => {
  const { fileWithUrl } = useFileUrl({ file });
  const { fileWithMetadata, error } = useFileMetadata({
    file: fileWithUrl,
    augmentFunction: augmentVideoFile,
  });
  const {
    seekRef,
    volumeRef,
    isPlaying,
    adjustVolume,
    reset,
    startSeek,
    endSeek,
    setSeek,
    updateSeekFromPlayback,
    refreshControls,
    durationDisplay = 0,
    currentTimeDisplay = 0,
    seekTimeDisplay = 0,
    isSeeking,
    isSeekTimeCountingDown,
    mediaControllerRef,
    handleAction,
    filenameRef,
    formId,
  } = useMediaControls<HTMLVideoElement>({
    controllerRef: forwardedRef,
  });

  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="h-full relative col-span-2">
        {
          typeof fileWithMetadata.url === 'string'
          && (
            <div
              className="w-full h-full bg-black flex flex-col items-stretch"
              data-testid="preview"
              key={`${fileWithMetadata.url}:${fileWithMetadata.type}`}
            >
              <div
                className="w-full flex-auto relative"
              >
                <video
                  {...etcProps}
                  className="sm: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"
                  ref={mediaControllerRef}
                  onLoadedMetadata={refreshControls}
                  onDurationChange={refreshControls}
                  onEnded={reset}
                  onTimeUpdate={updateSeekFromPlayback}
                  data-testid="preview"
                  controls={!clientSide}
                >
                  <source
                    src={fileWithMetadata.url}
                    type={fileWithMetadata.type}
                  />
                  Video playback not supported.
                </video>
                <button
                  className="absolute w-full h-full top-0 left-0"
                  type="submit"
                  name="action"
                  value="togglePlayback"
                  form={formId}
                  tabIndex={-1}
                >
                  <span className="sr-only">
                    {isPlaying ? 'Pause' : 'Play'}
                  </span>
                </button>
              </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="flex-auto text-base"
                        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
                      ref={volumeRef}
                      max={1}
                      min={0}
                      onChange={adjustVolume}
                      className="flex-auto"
                      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?.width === 'number'
            && typeof fileWithMetadata.metadata?.height === 'number'
            && {
              key: 'Pixel Dimensions',
              valueProps: {
                children: `${formatNumeral(fileWithMetadata.metadata.width)} × ${formatNumeral(fileWithMetadata.metadata.height)} pixel(s)`,
              },
            },
          ]}
        />
        {
          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 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>
  );
});

VideoFilePreview.displayName = 'VideoFilePreview';
