"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUpgradedAbi = exports.getProposedFacetCuts = exports.FacetCutAction = void 0;
const ethers_1 = require("ethers");
const utils_1 = require("hardhat-deploy/dist/src/utils");
var FacetCutAction;
(function (FacetCutAction) {
    FacetCutAction[FacetCutAction["Add"] = 0] = "Add";
    FacetCutAction[FacetCutAction["Replace"] = 1] = "Replace";
    FacetCutAction[FacetCutAction["Remove"] = 2] = "Remove";
})(FacetCutAction = exports.FacetCutAction || (exports.FacetCutAction = {}));
// NOTE: facet implementations *must* be deployed before calling this function
// INCLUDING the `_DefaultDiamondLoupeFacet`
const getProposedFacetCuts = async (facets, current) => {
    // Get existing facets + selectors
    const oldFacets = await current.facets();
    const oldSelectors = [];
    const oldSelectorsFacetAddress = {};
    for (const oldFacet of oldFacets) {
        for (const selector of oldFacet.functionSelectors) {
            oldSelectors.push(selector);
            // console.log(`existing facet address:`, oldFacet.facetAddress);
            oldSelectorsFacetAddress[selector] = oldFacet.facetAddress;
        }
    }
    let changesDetected = false;
    // Retrieve new facet selectors
    const newSelectors = [];
    const facetSnapshot = [];
    for (const facet of facets) {
        // NOTE: modified from: https://github.com/wighawag/hardhat-deploy/blob/3d08a33a6ae9404bf56187c4f49ec359427672eb/src/helpers.ts#L1792-L2443
        // NOTE: update if linkedData / libraries / facetArgs are included in deploy script
        // Update selectors and snapshot
        const functionSelectors = Object.values(facet.contract.interface.functions)
            .map((fragment) => {
            return facet.contract.interface.getSighash(fragment.format());
        })
            .filter((x) => !!x);
        facetSnapshot.push({
            name: facet.name,
            facetAddress: facet.contract.address,
            functionSelectors,
        });
        newSelectors.push(...functionSelectors);
    }
    // Find selectors to add and selectors to replace
    const facetCuts = [];
    for (const newFacet of facetSnapshot) {
        const selectorsToAdd = [];
        const selectorsToReplace = [];
        for (const selector of newFacet.functionSelectors) {
            if (oldSelectors.indexOf(selector) >= 0) {
                if (oldSelectorsFacetAddress[selector].toLowerCase() !== newFacet.facetAddress.toLowerCase()) {
                    selectorsToReplace.push(selector);
                }
            }
            else {
                selectorsToAdd.push(selector);
            }
        }
        if (selectorsToReplace.length > 0) {
            changesDetected = true;
            facetCuts.push({
                name: newFacet.name,
                facetAddress: newFacet.facetAddress,
                functionSelectors: selectorsToReplace,
                action: FacetCutAction.Replace,
            });
        }
        if (selectorsToAdd.length > 0) {
            changesDetected = true;
            facetCuts.push({
                name: newFacet.name,
                facetAddress: newFacet.facetAddress,
                functionSelectors: selectorsToAdd,
                action: FacetCutAction.Add,
            });
        }
        if (selectorsToAdd.length > 0) {
            console.log("trying to add:", selectorsToAdd);
        }
        if (selectorsToReplace.length > 0) {
            console.log("trying to replace:", selectorsToReplace);
        }
    }
    // Get facet selectors to delete
    const selectorsToDelete = [];
    for (const selector of oldSelectors) {
        if (newSelectors.indexOf(selector) === -1) {
            selectorsToDelete.push(selector);
        }
    }
    if (selectorsToDelete.length > 0) {
        console.log("trying to remove:", selectorsToDelete);
    }
    if (selectorsToDelete.length > 0) {
        changesDetected = true;
        facetCuts.unshift({
            name: "unknown",
            facetAddress: "0x0000000000000000000000000000000000000000",
            functionSelectors: selectorsToDelete,
            action: FacetCutAction.Remove,
        });
    }
    // If no changes detected, do nothing
    if (!changesDetected || facetCuts.length === 0) {
        // console.log(`no diamond upgrade proposal needed`);
        return []; // no cuts to propose
    }
    // Make sure this isnt a duplicate proposal (i.e. you aren't just resetting times)
    const acceptanceTime = await current.getAcceptanceTime(facetCuts, ethers_1.constants.AddressZero, "0x");
    if (!acceptanceTime.isZero()) {
        // console.log(`cut has already been proposed`);
        return []; // already proposed, nothing to do
    }
    // console.log("calling propose with", facetCuts);
    // Propose facet cut
    return facetCuts;
};
exports.getProposedFacetCuts = getProposedFacetCuts;
/**
 *
 * @param prev The abi of the contract prior to the upgrade execution
 */
const getUpgradedAbi = (facets, prev) => {
    // Shortcircuit if no facets
    if (facets.length === 0) {
        return prev;
    }
    // Merge all facet abis
    let abi = prev.concat([]);
    for (const facet of facets) {
        abi = (0, utils_1.mergeABIs)([abi, facet.contract.abi], {
            check: true,
            skipSupportsInterface: false,
        });
    }
    return abi;
};
exports.getUpgradedAbi = getUpgradedAbi;
//# sourceMappingURL=getProposedFacetCuts.js.map