import * as React from 'react';
import {augmentBinaryFile, getMimeTypeDescription} from '@/utils/blob';
import {formatFileSize, formatNumeral} from '@/utils/numeral';
import {useFileMetadata, useFileUrl} from '@/categories/blob/react';
import clsx from 'clsx';
import {KeyValueTable} from '@/categories/information/react';
import {BinaryDataCanvas} from '@modal-soft/react-binary-data-canvas';
import {useEnhanced} from '@modal-soft/react-utils';
import {CommonPreviewProps} from '@/categories/blob/react/common';

export type BinaryFilePreviewDerivedElement = HTMLDivElement;

export interface BinaryFilePreviewProps<F extends Partial<File> = Partial<File>>
  extends React.HTMLProps<BinaryFilePreviewDerivedElement>, CommonPreviewProps<F> {}

export const BinaryFilePreview = React.forwardRef<BinaryFilePreviewDerivedElement, BinaryFilePreviewProps>(({
  file,
  className,
  style,
  enhanced: enhancedProp = false,
  ...etcProps
}, forwardedRef) => {
  const { fileWithUrl } = useFileUrl({ file });
  const { fileWithMetadata, error } = useFileMetadata({
    file: fileWithUrl,
    augmentFunction: augmentBinaryFile,
  });
  const { enhanced } = useEnhanced({ enhanced: enhancedProp });

  if (!fileWithMetadata) {
    return null;
  }

  return (
    <div
      className={clsx(
        'flex flex-col sm:grid sm:grid-cols-3 gap-8 w-full',
        className,
      )}
      style={style}
      key={`${fileWithMetadata?.url ?? ''}:${fileWithMetadata?.type ?? ''}`}
    >
      <div className="h-full relative">
        <div className="absolute top-0 left-0 w-full h-full">
          {
            fileWithMetadata.metadata && (fileWithMetadata.metadata?.contents instanceof ArrayBuffer)
            && (
              <div
                {...etcProps}
                data-testid="preview"
                role="presentation"
                className="w-full h-full select-none overflow-hidden text-xs"
                ref={forwardedRef}
                key={`${fileWithMetadata.url}:${fileWithMetadata.type}`}
              >
                <BinaryDataCanvas
                  arrayBuffer={fileWithMetadata.metadata?.contents}
                  className="overflow-visible binary-file-preview"
                  headers
                  byteClassName={(byte, index) => {
                    if (byte < 0x10) {
                      return 'x00';
                    }
                    if (byte < 0x20) {
                      return 'x10';
                    }
                    if (byte < 0x30) {
                      return 'x20';
                    }
                    if (byte < 0x40) {
                      return 'x30';
                    }
                    if (byte < 0x50) {
                      return 'x40';
                    }
                    if (byte < 0x60) {
                      return 'x50';
                    }
                    if (byte < 0x70) {
                      return 'x60';
                    }
                    if (byte < 0x80) {
                      return 'x70';
                    }
                    if (byte < 0x90) {
                      return 'x80';
                    }
                    if (byte < 0xA0) {
                      return 'x90';
                    }
                    if (byte < 0xB0) {
                      return 'xa0';
                    }
                    if (byte < 0xC0) {
                      return 'xb0';
                    }
                    if (byte < 0xD0) {
                      return 'xc0';
                    }
                    if (byte < 0xE0) {
                      return 'xd0';
                    }
                    if (byte < 0xF0) {
                      return 'xe0';
                    }
                    return 'xf0';
                  }}
                />
              </div>
            )
          }
        </div>
      </div>
      <div className="col-span-2 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: {
                children: fileWithMetadata.name,
                title: fileWithMetadata.name,
              },
            },
            (enhanced && 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)',
              },
            },
            (enhanced && 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)',
              },
            },
          ]}
        />
      </div>
    </div>
  );
});

BinaryFilePreview.displayName = 'BinaryFilePreview';
