/**
 * Compare two script byte arrays for equality.
 */
function scriptsEqual(a, b) {
    if (a.length !== b.length)
        return false;
    for (let i = 0; i < a.length; i++) {
        if (a[i] !== b[i])
            return false;
    }
    return true;
}
/**
 * Find a definite descriptor in the descriptor map that matches the given script.
 * @param script
 * @param descriptorMap
 */
function findDescriptorWithoutDerivation(script, descriptorMap) {
    for (const descriptor of descriptorMap.values()) {
        if (!descriptor.hasWildcard()) {
            if (scriptsEqual(descriptor.scriptPubkey(), script)) {
                return { descriptor, index: undefined };
            }
        }
    }
    return undefined;
}
/**
 * Find a descriptor in the descriptor map that matches the given script and derivation index.
 * @param script
 * @param index
 * @param descriptorMap
 * @returns DescriptorWithIndex if found, undefined otherwise
 */
function findDescriptorForDerivationIndex(script, index, descriptorMap) {
    for (const descriptor of descriptorMap.values()) {
        if (descriptor.hasWildcard() &&
            scriptsEqual(descriptor.atDerivationIndex(index).scriptPubkey(), script)) {
            return { descriptor, index };
        }
    }
    return undefined;
}
function getDerivationIndexFromPath(path) {
    const indexStr = path.split("/").pop();
    if (!indexStr) {
        throw new Error(`Invalid derivation path ${path}`);
    }
    const index = parseInt(indexStr, 10);
    if (index.toString() !== indexStr) {
        throw new Error(`Invalid derivation path ${path}`);
    }
    return index;
}
/**
 * Wrapper around findDescriptorForDerivationPath that tries multiple derivation paths.
 * @param script
 * @param derivationPaths
 * @param descriptorMap
 */
function findDescriptorForAnyDerivationPath(script, derivationPaths, descriptorMap) {
    const derivationIndexSet = new Set(derivationPaths.map((p) => getDerivationIndexFromPath(p)));
    for (const index of [...derivationIndexSet]) {
        const desc = findDescriptorForDerivationIndex(script, index, descriptorMap);
        if (desc) {
            return desc;
        }
    }
    return undefined;
}
function getDerivationPaths(v) {
    if ("bip32Derivation" in v && v.bip32Derivation && v.bip32Derivation.length > 0) {
        return v.bip32Derivation.map((v) => v.path);
    }
    if ("tapBip32Derivation" in v && v.tapBip32Derivation && v.tapBip32Derivation.length > 0) {
        return v.tapBip32Derivation.map((v) => v.path).filter((v) => v !== "" && v !== "m");
    }
    return undefined;
}
/**
 * @param input
 * @param descriptorMap
 * @returns DescriptorWithIndex for the input if found, undefined otherwise
 */
export function findDescriptorForInput(input, descriptorMap) {
    const script = input.witnessUtxo?.script;
    if (!script) {
        throw new Error("Missing script");
    }
    const derivationPaths = getDerivationPaths(input) ?? [];
    return (findDescriptorWithoutDerivation(script, descriptorMap) ??
        findDescriptorForAnyDerivationPath(script, derivationPaths, descriptorMap));
}
/**
 * @param script - the output script
 * @param output - the PSBT output
 * @param descriptorMap
 * @returns DescriptorWithIndex for the output if found, undefined otherwise
 */
export function findDescriptorForOutput(script, output, descriptorMap) {
    const derivationPaths = getDerivationPaths(output);
    return (findDescriptorWithoutDerivation(script, descriptorMap) ??
        (derivationPaths === undefined
            ? undefined
            : findDescriptorForAnyDerivationPath(script, derivationPaths, descriptorMap)));
}
