import { DefaultObservableValue, Errors, isInstanceOf, isNotUndefined, Notifier, Progress, RuntimeNotifier, UUID } from "@opendaw/lib-std";
import { Files } from "@opendaw/lib-dom";
import { Promises } from "@opendaw/lib-runtime";
export class AssetService {
    notifier = new Notifier();
    subscribe(observer) { return this.notifier.subscribe(observer); }
    async browse(multiple) {
        return this.browseFiles(multiple, this.filePickerOptions);
    }
    async replaceMissingFiles(boxGraph, manager) {
        const available = await this.collectAllFiles();
        const boxes = boxGraph.boxes().filter(box => isInstanceOf(box, this.boxType));
        if (boxes.length === 0) {
            return;
        }
        for (const box of boxes) {
            const uuid = box.address.uuid;
            const uuidAsString = UUID.toString(uuid);
            if (isNotUndefined(available.find(({ uuid }) => uuid === uuidAsString))) {
                continue;
            }
            const approved = await RuntimeNotifier.approve({
                headline: "Missing Asset",
                message: `Could not find ${this.nameSingular} '${box.fileName.getValue()}'`,
                cancelText: "Ignore",
                approveText: "Browse"
            });
            if (!approved) {
                continue;
            }
            const { error, status, value: files } = await Promises.tryCatch(Files.open({ ...this.filePickerOptions, multiple: false }));
            if (status === "rejected") {
                if (Errors.isAbort(error) || Errors.isNotAllowed(error)) {
                    return;
                }
                console.warn(`File browse failed: ${error}`);
                await RuntimeNotifier.info({ headline: "File Access Error", message: String(error) });
                return;
            }
            if (files.length === 0) {
                return;
            }
            const readResult = await Promises.tryCatch(files[0].arrayBuffer());
            if (readResult.status === "rejected") {
                await RuntimeNotifier.info({
                    headline: "File Read Error",
                    message: `'${files[0].name}' could not be read. The file may be on an inaccessible location.`
                });
                continue;
            }
            const importResult = await Promises.tryCatch(this.importFile({
                uuid, name: files[0].name, arrayBuffer: readResult.value, progressHandler: Progress.Empty
            }));
            if (importResult.status === "rejected") {
                await RuntimeNotifier.info({
                    headline: `${this.nameSingular} Import Failed`,
                    message: `'${files[0].name}' could not be imported: ${String(importResult.error)}`
                });
                continue;
            }
            await RuntimeNotifier.info({
                headline: "Replaced Asset",
                message: `${importResult.value.name} has been replaced`
            });
            manager.invalidate(uuid);
        }
    }
    async browseFiles(multiple, filePickerSettings) {
        const { error, status, value: files } = await Promises.tryCatch(Files.open({ ...filePickerSettings, multiple }));
        if (status === "rejected") {
            if (Errors.isAbort(error) || Errors.isNotAllowed(error)) {
                return [];
            }
            console.warn(`File browse failed: ${error}`);
            await RuntimeNotifier.info({ headline: "File Access Error", message: String(error) });
            return [];
        }
        const progress = new DefaultObservableValue(0.0);
        const dialog = RuntimeNotifier.progress({
            headline: `Importing ${files.length === 1 ? this.nameSingular : this.namePlural}...`, progress
        });
        const progressHandler = Progress.split(value => progress.setValue(value), files.length);
        const rejected = [];
        const imported = [];
        for (const [index, file] of files.entries()) {
            const readResult = await Promises.tryCatch(file.arrayBuffer());
            if (readResult.status === "rejected") {
                rejected.push(`'${file.name}' could not be read`);
                continue;
            }
            const { status, value, error } = await Promises.tryCatch(this.importFile({
                name: file.name,
                arrayBuffer: readResult.value,
                progressHandler: progressHandler[index]
            }));
            if (status === "rejected") {
                rejected.push(String(error));
            }
            else {
                imported.push(value);
            }
        }
        dialog.terminate();
        if (rejected.length > 0) {
            await RuntimeNotifier.info({
                headline: `${this.nameSingular} Import Issues`,
                message: `${rejected.join(", ")} could not be imported.`
            });
        }
        return imported;
    }
}
