// Copyright 2024 Adobe. All rights reserved.
// This file is licensed to you under the Apache License,
// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
// or the MIT license (http://opensource.org/licenses/MIT),
// at your option.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { getNeonBinary } from "./binary.js";
import { IdentityAssertionSigner } from "./IdentityAssertion.js";
// c2pa-rs `DataHash` uses `#[serde(with = "serde_bytes")]` for `hash` and
// `pad`, which deserializes from a JSON array of u8 (not a `Buffer` object,
// whose default JSON shape is `{ type: "Buffer", data: [...] }`). Normalize
// any byte-like input to `number[]`. `pad` is a required field on the Rust
// side, so default it to an empty array if the caller omits it.
function bytesToNumberArray(input) {
    if (input === undefined)
        return [];
    if (Array.isArray(input))
        return input;
    return Array.from(input);
}
function serializeDataHash(dataHash) {
    return JSON.stringify({
        name: dataHash.name,
        alg: dataHash.alg,
        hash: bytesToNumberArray(dataHash.hash),
        pad: bytesToNumberArray(dataHash.pad),
        exclusions: dataHash.exclusions,
    });
}
export class Builder {
    constructor(builder) {
        this.builder = builder;
    }
    static new(settings) {
        const settingsStr = settings
            ? typeof settings === "string"
                ? settings
                : JSON.stringify(settings)
            : undefined;
        const builder = getNeonBinary().builderNew(settingsStr);
        return new Builder(builder);
    }
    static withJson(json, settings) {
        let jsonString;
        try {
            jsonString = JSON.stringify(json);
        }
        catch (error) {
            // TODO: errors should be standardized across JS and Node
            if (error instanceof Error) {
                throw new Error(`Failed to stringify JSON Manifest Definition: ${error.message}`);
            }
            throw new Error("Failed to stringify JSON Manifest Definition: Unknown error");
        }
        const settingsStr = settings
            ? typeof settings === "string"
                ? settings
                : JSON.stringify(settings)
            : undefined;
        const builder = getNeonBinary().builderWithJson(jsonString, settingsStr);
        return new Builder(builder);
    }
    setIntent(intent) {
        const intentString = JSON.stringify(intent);
        getNeonBinary().builderSetIntent.call(this.builder, intentString);
    }
    setNoEmbed(noEmbed = true) {
        getNeonBinary().builderSetNoEmbed.call(this.builder, noEmbed);
    }
    setRemoteUrl(remoteUrl) {
        getNeonBinary().builderSetRemoteUrl.call(this.builder, remoteUrl);
    }
    addAction(actionJson) {
        return getNeonBinary().builderAddAction.call(this.builder, actionJson);
    }
    addAssertion(label, assertion, assertionKind) {
        return getNeonBinary().builderAddAssertion.call(this.builder, label, assertion, assertionKind);
    }
    addResource(uri, resource) {
        return __awaiter(this, void 0, void 0, function* () {
            return getNeonBinary().builderAddResource.call(this.builder, uri, resource);
        });
    }
    addIngredient(ingredientJson, ingredient) {
        return __awaiter(this, void 0, void 0, function* () {
            if (ingredient) {
                return getNeonBinary().builderAddIngredientFromAsset.call(this.builder, ingredientJson, ingredient);
            }
            else {
                return getNeonBinary().builderAddIngredient.call(this.builder, ingredientJson);
            }
        });
    }
    addIngredientFromReader(reader) {
        const readerHandle = reader.getHandle();
        const result = getNeonBinary().builderAddIngredientFromReader.call(this.builder, readerHandle);
        return JSON.parse(result);
    }
    toArchive(asset) {
        return __awaiter(this, void 0, void 0, function* () {
            return getNeonBinary().builderToArchive.call(this.builder, asset);
        });
    }
    static fromArchive(asset, settings) {
        return __awaiter(this, void 0, void 0, function* () {
            const settingsStr = settings
                ? typeof settings === "string"
                    ? settings
                    : JSON.stringify(settings)
                : undefined;
            return new Builder(yield getNeonBinary().builderFromArchive(asset, settingsStr));
        });
    }
    sign(signer, input, output) {
        return getNeonBinary().builderSign.call(this.builder, signer.getHandle(), input, output);
    }
    signFile(signer, filePath, output) {
        const input = { path: filePath };
        return getNeonBinary().builderSign.call(this.builder, signer.getHandle(), input, output);
    }
    signConfigAsync(callback, signerConfig, input, output) {
        return __awaiter(this, void 0, void 0, function* () {
            return getNeonBinary()
                .builderSignConfigAsync.call(this.builder, callback, signerConfig, input, output)
                .then((result) => {
                // output is a buffer and result is the manifest and the signed asset.
                if ("buffer" in output) {
                    if ("signedAsset" in result && "manifest" in result) {
                        output.buffer = result.signedAsset;
                        return result.manifest;
                    }
                    else {
                        throw new Error("Unexpected result for DestinationBuffer");
                    }
                }
                else {
                    // output is a file and result is the bytes of the manifest.
                    return result;
                }
            })
                .catch((error) => {
                throw error;
            });
        });
    }
    signAsync(signer, input, output) {
        return __awaiter(this, void 0, void 0, function* () {
            const neonHandle = signer.getHandle();
            const isIdentity = signer instanceof IdentityAssertionSigner;
            const neonFn = isIdentity
                ? getNeonBinary().builderIdentitySignAsync
                : getNeonBinary().builderSignAsync;
            return neonFn
                .call(this.builder, neonHandle, input, output)
                .then((result) => {
                // output is a buffer and result is the manifest and the signed asset.
                if ("buffer" in output) {
                    if ("signedAsset" in result && "manifest" in result) {
                        output.buffer = result.signedAsset;
                        return result.manifest;
                    }
                    else {
                        throw new Error("Unexpected result for DestinationBuffer");
                    }
                }
                else {
                    // output is a file and result is the bytes of the manifest.
                    return result;
                }
            })
                .catch((error) => {
                throw error;
            });
        });
    }
    signDataHashedEmbeddable(signer, dataHash, format) {
        return getNeonBinary().builderSignDataHashedEmbeddable.call(this.builder, signer.getHandle(), serializeDataHash(dataHash), format);
    }
    signDataHashedEmbeddableAsync(signer, dataHash, format) {
        return __awaiter(this, void 0, void 0, function* () {
            return getNeonBinary().builderSignDataHashedEmbeddableAsync.call(this.builder, signer.getHandle(), serializeDataHash(dataHash), format);
        });
    }
    getManifestDefinition() {
        return JSON.parse(getNeonBinary().builderManifestDefinition.call(this.builder));
    }
    updateManifestProperty(property, value) {
        getNeonBinary().builderUpdateManifestProperty.call(this.builder, property, value);
    }
    addRedaction(uri, reason) {
        getNeonBinary().builderAddRedaction.call(this.builder, uri, reason);
    }
    getHandle() {
        return this.builder;
    }
}
