import { useState } from 'react';

import { type FileCardType } from '@material-hu/components/design-system/FileCard/types';
import Uploader from '@material-hu/components/design-system/Uploader';

import {
  getDimensions,
  getVideoMetadata,
  uploadOneFile,
} from 'src/services/attachments';
import { FileTypes } from 'src/types/attachments';
import { bytesFrom } from 'src/utils/bytes';

const MAX_FILE_ATTACHMENTS = 10;
const MAX_SIZE_PER_FILE = bytesFrom(100, 'MB');

type MediaValue = {
  images?: Record<string, unknown>[];
  videos?: Record<string, unknown>[];
};

type Props = {
  formValue: MediaValue | undefined;
  onChange: (value: MediaValue | undefined) => void;
  disabled: boolean;
  setUploadingFiles: React.Dispatch<
    React.SetStateAction<Record<string, boolean>>
  >;
  fieldName: string;
};

const MediaUploader = ({
  formValue,
  onChange,
  disabled,
  setUploadingFiles,
  fieldName,
}: Props) => {
  const [loadingCards, setLoadingCards] = useState<FileCardType[]>([]);

  const allMedia = [...(formValue?.images || []), ...(formValue?.videos || [])];
  const mediaByUrl = new Map(allMedia.map(m => [m.url, m]));

  const settledCards = allMedia.map(m => ({
    status: 'success' as const,
    attachment: {
      url: m.url,
      name: m.name,
      type: m.type || FileTypes.IMAGE,
      size: m.size || '0 B',
      bytes: 0,
    },
  }));

  const handleChange = (cards: FileCardType[]) => {
    const uploading = cards.filter(c => c.status === 'uploading');
    setLoadingCards(uploading);

    const images: Record<string, unknown>[] = [];
    const videos: Record<string, unknown>[] = [];

    for (const c of cards) {
      const att = c.attachment;
      if (c.status !== 'success' || !att) continue;

      const existing = mediaByUrl.get(att.url);
      if (att.type === FileTypes.IMAGE) {
        images.push(
          existing || {
            name: att.name,
            size: att.size,
            mime: c.file?.type || '',
            fileObject: c.file || {},
            type: FileTypes.IMAGE,
            toUpload: true,
            url: att.url,
            width: (att as Record<string, unknown>).width,
            height: (att as Record<string, unknown>).height,
          },
        );
      } else {
        videos.push(
          existing || {
            name: att.name,
            size: att.size,
            mime: c.file?.type || '',
            fileObject: c.file || {},
            type: FileTypes.VIDEO,
            toUpload: true,
            url: att.url,
          },
        );
      }
    }

    if (images.length === 0 && videos.length === 0) {
      onChange(undefined);
    } else {
      onChange({ images, videos });
    }
  };

  const handleUpload = async (file: File) => {
    setUploadingFiles(prev => ({ ...prev, [fieldName]: true }));
    try {
      const isImage = file.type.startsWith('image/');
      const type = isImage ? FileTypes.IMAGE : FileTypes.VIDEO;

      const dims = isImage
        ? await getDimensions(file)
        : await getVideoMetadata(file);

      const result = await uploadOneFile({
        name: file.name,
        fileObject: file,
        contentType: file.type,
        size: file.size,
        type,
        width: dims.width,
        height: dims.height,
      });

      return {
        status: 'success' as const,
        file,
        attachment: {
          url: result.url ?? '',
          name: result.name ?? '',
          type: result.type || type,
          size: result.size || '0 B',
          bytes: file.size,
          width: result.width,
          height: result.height,
        },
      };
    } catch {
      return { status: 'error' as const, file };
    } finally {
      setUploadingFiles(prev => {
        const next = { ...prev };
        delete next[fieldName];
        return next;
      });
    }
  };

  return (
    <Uploader
      // biome-ignore lint/suspicious/noExplicitAny: attachment has extra width/height fields for images
      value={[...settledCards, ...loadingCards] as any}
      onChange={handleChange}
      // biome-ignore lint/suspicious/noExplicitAny: return type has extended attachment fields
      uploadFunction={handleUpload as any}
      triggerOnChangeWhenUploading
      acceptedTypes={['image', 'video']}
      maxFiles={MAX_FILE_ATTACHMENTS}
      fileSizeLimit={MAX_SIZE_PER_FILE}
      disabled={disabled}
      readOnly={disabled}
    />
  );
};

export default MediaUploader;
