import { assertExists } from '@blockeditor/global/utils';
import {} from '@blockeditor/store';
import { toast } from '../_common/components/toast.js';
import { downloadBlob, withTempBlobData } from '../_common/utils/filesys.js';
import { humanFileSize } from '../_common/utils/math.js';
import { transformModel } from '../page-block/utils/operations/model.js';
import { AttachmentBlockModel, } from './attachment-model.js';
import { defaultAttachmentProps } from './attachment-model.js';
const DEFAULT_ATTACHMENT_NAME = 'affine-attachment';
export function cloneAttachmentProperties(model) {
    const clonedProps = {};
    for (const cur in defaultAttachmentProps) {
        const key = cur;
        // @ts-expect-error it's safe because we just cloned the props simply
        clonedProps[key] = model[key];
    }
    return clonedProps;
}
export async function getAttachmentBlob(model) {
    const blobManager = model.page.blob;
    const sourceId = model.sourceId;
    if (!sourceId)
        return null;
    const blob = await blobManager.get(sourceId);
    if (!blob)
        return null;
    return blob;
}
/**
 * Since the size of the attachment may be very large,
 * the download process may take a long time!
 */
export async function downloadAttachmentBlob(attachmentModel) {
    const attachment = await getAttachmentBlob(attachmentModel);
    if (!attachment) {
        toast('Failed to download attachment!');
        console.error('attachment load failed! sourceId:', attachmentModel.sourceId, 'BlobManager:', attachmentModel.page.blob);
        return;
    }
    downloadBlob(attachment, attachmentModel.name);
}
/**
 * Turn the attachment block into an image block.
 */
export async function turnIntoImage(model) {
    if (!model.page.schema.flavourSchemaMap.has('affine:image'))
        throw new Error('The image flavour is not supported!');
    const sourceId = model.sourceId;
    assertExists(sourceId);
    const { saveAttachmentData, getImageData } = withTempBlobData();
    saveAttachmentData(sourceId, { name: model.name });
    const imageConvertData = model.sourceId
        ? getImageData(model.sourceId)
        : undefined;
    const imageProp = {
        sourceId,
        caption: model.caption,
        size: model.size,
        ...imageConvertData,
    };
    transformModel(model, 'affine:image', imageProp);
}
/**
 * Turn the image block into a attachment block.
 */
export function turnImageIntoCardView(model, blob) {
    if (!model.page.schema.flavourSchemaMap.has('affine:attachment'))
        throw new Error('The attachment flavour is not supported!');
    if (!model.sourceId)
        throw new Error('Image data not aviailable');
    const sourceId = model.sourceId;
    assertExists(sourceId);
    const { saveImageData, getAttachmentData } = withTempBlobData();
    saveImageData(sourceId, { width: model.width, height: model.height });
    const attachmentConvertData = getAttachmentData(model.sourceId);
    const attachmentProp = {
        sourceId,
        name: DEFAULT_ATTACHMENT_NAME,
        size: blob.size,
        type: blob.type,
        caption: model.caption,
        embed: false,
        ...attachmentConvertData,
    };
    transformModel(model, 'affine:attachment', attachmentProp);
}
/**
 * This function will not verify the size of the file.
 *
 * NOTE: This function may take a long time!
 *
 * @internal
 */
async function uploadBlobForAttachment(page, attachmentModelId, blob) {
    const isLoading = isAttachmentLoading(attachmentModelId);
    if (isLoading) {
        throw new Error('the attachment is already uploading!');
    }
    setAttachmentLoading(attachmentModelId, true);
    const storage = page.blob;
    // The original file name can not be modified after the file is uploaded to the storage,
    // so we create a new file with a fixed name to prevent privacy leaks.
    // const anonymousFile = new File([file.slice(0, file.size)], 'anonymous', {
    //   type: file.type,
    // });
    try {
        // the uploading process may take a long time!
        const sourceId = await storage.set(blob);
        // await new Promise(resolve => setTimeout(resolve, 1000));
        const attachmentModel = page.getBlockById(attachmentModelId);
        if (!attachmentModel) {
            // It occurs when the block is deleted before the uploading process is finished.
            throw new Error('the attachment model is not found!');
        }
        if (!(attachmentModel instanceof AttachmentBlockModel)) {
            console.error(attachmentModel);
            throw new Error('the model is not an attachment model!');
        }
        page.withoutTransact(() => {
            page.updateBlock(attachmentModel, {
                sourceId,
            });
        });
    }
    catch (error) {
        console.error(error);
        if (error instanceof Error) {
            toast(`Failed to upload attachment! ${error.message || error.toString()}`);
        }
    }
    finally {
        setAttachmentLoading(attachmentModelId, false);
    }
    return attachmentModelId;
}
/**
 * Add a new attachment block before / after the specified block.
 */
export async function addSiblingAttachmentBlock(file, maxFileSize, model, place = 'after') {
    if (file.size > maxFileSize) {
        toast(`You can only upload files less than ${humanFileSize(maxFileSize, true, 0)}`);
        return null;
    }
    const page = model.page;
    const blockId = page.generateBlockId();
    const props = {
        id: blockId,
        flavour: 'affine:attachment',
        name: file.name,
        size: file.size,
        type: file.type,
        embed: false,
    };
    const [newBlockId] = page.addSiblingBlocks(model, [props], place);
    // Do not add await here, because upload may take a long time, we want to do it in the background.
    return uploadBlobForAttachment(page, newBlockId, file);
}
const attachmentLoadingMap = new Set();
export function setAttachmentLoading(modelId, loading) {
    if (loading) {
        attachmentLoadingMap.add(modelId);
    }
    else {
        attachmentLoadingMap.delete(modelId);
    }
}
export function isAttachmentLoading(modelId) {
    return attachmentLoadingMap.has(modelId);
}
/**
 * Use it with caution! This function may take a long time!
 *
 * @deprecated Use {@link getAttachmentBlob} instead.
 */
export async function hasBlob(storage, sourceId) {
    return !!(await storage.get(sourceId));
}
//# sourceMappingURL=utils.js.map