import { useState } from 'react';
import { useController, useFormContext } from 'react-hook-form';

import { type FileCardType } from '@design-system/FileCard/types';
import StateCard from '@design-system/StateCard';
import Uploader, { type UploaderProps } from '@design-system/Uploader';
import { type FileType } from '@design-system/Uploader/types';
import { processFile } from '@utils/attachments';
import { bytesFrom, sizeToBytes } from '@utils/bytes';
import {
  type Attachment,
  type FileAsset,
  type FormFile,
} from 'types/attachments';

import { type DynamicFormValues } from '../../types';
import { getInitialFiles } from '../../utils/extraUtils';

const DEFAULT_ACCEPTED_TYPES: FileType[] = ['pdf', 'image'];
const DEFAULT_FILE_SIZE_LIMIT = bytesFrom(50, 'MB');
const DEFAULT_MAX_FILES = 5;

export type InputFileProps = {
  name: string;
  disabled?: boolean;
  readOnly?: boolean;
  initialFiles: FileAsset[];
  uploadFn?: (files: FormFile[]) => Promise<FileAsset[]>;
  acceptedTypes?: FileType[];
  fileSizeLimit?: number;
  maxFiles?: number;
  slotProps?: {
    uploader?: Partial<UploaderProps>;
  };
};

// Custom type to track the files on remove operations
type CustomFileCardAttachment = Attachment & { fileAssetReferenceId: number };

const InputFile = ({
  name,
  disabled,
  initialFiles,
  readOnly,
  uploadFn,
  acceptedTypes = DEFAULT_ACCEPTED_TYPES,
  fileSizeLimit = DEFAULT_FILE_SIZE_LIMIT,
  maxFiles = DEFAULT_MAX_FILES,
  slotProps,
}: InputFileProps) => {
  const [value, setValue] = useState<FileCardType[]>(
    getInitialFiles(initialFiles),
  );
  const {
    control,
    getValues,
    setValue: setFormValue,
    clearErrors,
  } = useFormContext<DynamicFormValues>();
  const { fieldState } = useController({ name, control: control });

  const handleUpload = async (file: File): Promise<FileCardType> => {
    try {
      if (!uploadFn) {
        throw new Error('uploadFn is required');
      }
      // Process the file to get the correct type
      const processedFile = await processFile(file);

      // Upload the file to S3
      const [uploadedFile] = await uploadFn?.([processedFile]);

      // Create the attachment object to be able to use the Uploader component
      const attachment: CustomFileCardAttachment = {
        url: uploadedFile.url,
        size: uploadedFile.size,
        bytes: sizeToBytes(uploadedFile.size),
        name: file.name,
        type: file.type,
        fileAssetReferenceId: uploadedFile.id,
      };

      // Get and set the new file to the form below "FileAsset" type
      const currentFormFiles = (getValues(name) as FileAsset[]) ?? [];
      setFormValue(name, [...currentFormFiles, uploadedFile]);

      return { status: 'success', attachment };
    } catch (error) {
      return { status: 'error', file };
    }
  };

  const handleOnChange = (files: FileCardType[]) => {
    clearErrors(name);
    setValue(files);

    // Get the current form
    const currentFormFiles = (getValues(name) as FileAsset[]) ?? [];

    // Get the reference file ids to be able to filter the files on remove operations
    const referencesFileIds = new Set(
      files.map(
        file =>
          (file.attachment as CustomFileCardAttachment)?.fileAssetReferenceId,
      ),
    );

    // Filter the files on remove operations
    const newFormFiles = currentFormFiles.filter(file =>
      referencesFileIds.has(file.id),
    );

    // Set the new form files to the form below "FileAsset" type
    setFormValue(name, newFormFiles);
  };

  if (!uploadFn && !readOnly) {
    return (
      <StateCard
        description="`uploadFn` is required to use this component"
        title={'No upload function provided'}
        variant="error"
      />
    );
  }

  if (readOnly && !value.length) {
    return null;
  }

  return (
    <Uploader
      error={!!fieldState.error}
      helperText={fieldState.error?.message}
      disabled={disabled}
      readOnly={readOnly}
      acceptedTypes={acceptedTypes}
      fileSizeLimit={fileSizeLimit}
      maxFiles={readOnly || disabled ? value.length : maxFiles}
      slotProps={{ fileCard: { disabled } }}
      uploadFunction={handleUpload}
      onChange={handleOnChange}
      value={value}
      showUploadButtonOnMaxFiles={false}
      triggerOnChangeWhenUploading={true}
      {...slotProps?.uploader}
    />
  );
};

export default InputFile;
