import { Option, safeExecute, tryCatch, UUID } from "@opendaw/lib-std";
import { ProjectSkeleton } from "@opendaw/studio-adapters";
import { Promises } from "@opendaw/lib-runtime";
import { Workers } from "../Workers";
import { ProjectPaths } from "./ProjectPaths";
export var ProjectStorage;
(function (ProjectStorage) {
    ProjectStorage.listProjects = async ({ includeCover, includeProject, progress } = {}) => {
        return Workers.Opfs.list(ProjectPaths.Folder)
            .then(files => Promise.all(files.filter(file => file.kind === "directory")
            .map(async ({ name }, index, { length }) => {
            safeExecute(progress, (index + 1) / length);
            const uuid = UUID.parse(name);
            const array = await Workers.Opfs.read(ProjectPaths.projectMeta(uuid));
            return {
                uuid,
                meta: JSON.parse(new TextDecoder().decode(array)),
                cover: includeCover ? (await ProjectStorage.loadCover(uuid)).unwrapOrUndefined() : undefined,
                project: includeProject ? await ProjectStorage.loadProject(uuid) : undefined
            };
        })));
    };
    ProjectStorage.loadProject = async (uuid) => {
        return Workers.Opfs.read(ProjectPaths.projectFile(uuid)).then(array => array.buffer);
    };
    ProjectStorage.loadMeta = async (uuid) => {
        return Workers.Opfs.read(ProjectPaths.projectMeta(uuid)).then(array => array.buffer);
    };
    ProjectStorage.loadCover = async (uuid) => {
        return Workers.Opfs.read(ProjectPaths.projectCover(uuid))
            .then(array => Option.wrap(array.buffer), () => Option.None);
    };
    ProjectStorage.listUsedAssets = async (type) => {
        console.debug("listUsedAssets", type.name);
        const result = new Map();
        const exactBuffer = (bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
        const files = await Workers.Opfs.list(ProjectPaths.Folder);
        for (const { name: folder } of files.filter(file => file.kind === "directory")) {
            const uuid = UUID.parse(folder);
            const projectBytes = await Promises.tryCatch(Workers.Opfs.read(ProjectPaths.projectFile(uuid)));
            if (projectBytes.status === "rejected") {
                continue;
            }
            const metaBytes = await Promises.tryCatch(Workers.Opfs.read(ProjectPaths.projectMeta(uuid)));
            const projectName = metaBytes.status === "rejected" ? folder
                : JSON.parse(new TextDecoder().decode(metaBytes.value)).name;
            const decoded = tryCatch(() => ProjectSkeleton.decode(exactBuffer(projectBytes.value)));
            if (decoded.status === "failure") {
                console.warn(`listUsedAssets: failed to decode project '${projectName}'`, decoded.error);
                continue;
            }
            for (const box of decoded.value.boxGraph.boxes()) {
                if (!(box instanceof type)) {
                    continue;
                }
                const key = UUID.toString(box.address.uuid);
                const list = result.get(key) ?? [];
                if (!list.includes(projectName)) {
                    list.push(projectName);
                }
                result.set(key, list);
            }
        }
        return result;
    };
    ProjectStorage.deleteProject = async (uuid) => {
        const array = await ProjectStorage.loadTrashedIds();
        array.push(UUID.toString(uuid));
        const trash = new TextEncoder().encode(JSON.stringify(array));
        await Workers.Opfs.write(`${ProjectPaths.Folder}/trash.json`, trash);
        await Workers.Opfs.delete(ProjectPaths.projectFolder(uuid));
    };
    ProjectStorage.loadTrashedIds = async () => {
        const { status, value } = await Promises.tryCatch(Workers.Opfs.read(`${ProjectPaths.Folder}/trash.json`));
        return status === "rejected" ? [] : JSON.parse(new TextDecoder().decode(value));
    };
})(ProjectStorage || (ProjectStorage = {}));
