"use strict";
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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnreachableCaseError = exports.createAuthorizationResolver = exports.assertAddRelayerPayingKeyAuthorizationValid = exports.assertJoinIdentityAuthorizationValid = exports.assertTransferAssetOwnershipAuthorizationValid = exports.assertTransferTickerAuthorizationValid = exports.assertAttestPrimaryKeyAuthorizationValid = exports.assertPrimaryKeyRotationAuthorizationValid = exports.assertAuthorizationRequestValid = exports.assertRequirementsNotTooComplex = exports.isFullGroupType = exports.assertDistributionDatesValid = exports.assertCaCheckpointValid = exports.assertCaTaxWithholdingsValid = exports.assertCaTargetsValid = exports.assertDistributionOpen = exports.assertSecondaryKeys = exports.assertPortfolioExists = exports.assertInstructionValid = void 0;
const internal_1 = require("../../internal");
const types_1 = require("../../types");
const conversion_1 = require("../../utils/conversion");
const internal_2 = require("../../utils/internal");
// import { Proposal } from '~/internal';
// import { ProposalStage, ProposalState } from '~/api/entities/Proposal/types';
// import { Context, PolymeshError } from '~/internal';
// import { ErrorCode } from '~/types';
// /**
//  * @hidden
//  */
// export async function assertProposalUnlocked(pipId: BigNumber, context: Context): Promise<void> {
//   const proposal = new Proposal({ pipId }, context);
//   const [details, stage] = await Promise.all([proposal.getDetails(), proposal.getStage()]);
//   const { lastState } = details;
//   if (lastState !== ProposalState.Pending) {
//     throw new PolymeshError({
//       code: ErrorCode.ValidationError,
//       message: 'The proposal must be in pending state',
//     });
//   }
//   if (stage !== ProposalStage.CoolOff) {
//     throw new PolymeshError({
//       code: ErrorCode.ValidationError,
//       message: 'The proposal must be in its cool-off period',
//     });
//   }
// }
/**
 * @hidden
 */
function assertInstructionValid(instruction, context) {
    return __awaiter(this, void 0, void 0, function* () {
        const details = yield instruction.details();
        const { status } = yield instruction.details();
        if (status !== types_1.InstructionStatus.Pending) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'The Instruction must be in pending state',
            });
        }
        if (details.type === types_1.InstructionType.SettleOnBlock) {
            const latestBlock = yield context.getLatestBlock();
            const { endBlock } = details;
            if (latestBlock >= endBlock) {
                throw new internal_1.PolymeshError({
                    code: types_1.ErrorCode.UnmetPrerequisite,
                    message: 'The Instruction cannot be modified; it has already reached its end block',
                    data: {
                        currentBlock: latestBlock,
                        endBlock,
                    },
                });
            }
        }
    });
}
exports.assertInstructionValid = assertInstructionValid;
/**
 * @hidden
 */
function assertPortfolioExists(portfolioId, context) {
    return __awaiter(this, void 0, void 0, function* () {
        const { did, number } = portfolioId;
        if (number) {
            const numberedPortfolio = new internal_1.NumberedPortfolio({ did, id: number }, context);
            const exists = yield numberedPortfolio.exists();
            if (!exists) {
                throw new internal_1.PolymeshError({
                    code: types_1.ErrorCode.DataUnavailable,
                    message: "The Portfolio doesn't exist",
                    data: {
                        did,
                        portfolioId: number,
                    },
                });
            }
        }
    });
}
exports.assertPortfolioExists = assertPortfolioExists;
/**
 * @hidden
 */
function assertSecondaryKeys(signerValues, secondaryKeys) {
    const notInTheList = [];
    signerValues.forEach(({ value: itemValue }) => {
        const isPresent = secondaryKeys
            .map(({ signer }) => conversion_1.signerToSignerValue(signer))
            .find(({ value }) => value === itemValue);
        if (!isPresent) {
            notInTheList.push(itemValue);
        }
    });
    if (notInTheList.length) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.UnmetPrerequisite,
            message: 'One of the Signers is not a Secondary Key for the Identity',
            data: {
                missing: notInTheList,
            },
        });
    }
}
exports.assertSecondaryKeys = assertSecondaryKeys;
/**
 * @hidden
 */
function assertDistributionOpen(paymentDate, expiryDate) {
    const now = new Date();
    if (paymentDate > now) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.UnmetPrerequisite,
            message: "The Distribution's payment date hasn't been reached",
            data: { paymentDate },
        });
    }
    if (expiryDate && expiryDate < now) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.UnmetPrerequisite,
            message: 'The Distribution has already expired',
            data: {
                expiryDate,
            },
        });
    }
}
exports.assertDistributionOpen = assertDistributionOpen;
/**
 * @hidden
 */
function assertCaTargetsValid(targets, context) {
    const { maxTargetIds } = context.polymeshApi.consts.corporateAction;
    const maxTargets = conversion_1.u64ToBigNumber(maxTargetIds);
    if (maxTargets.lt(targets.identities.length)) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.ValidationError,
            message: 'Too many target Identities',
            data: {
                maxTargets,
            },
        });
    }
}
exports.assertCaTargetsValid = assertCaTargetsValid;
/**
 * @hidden
 */
function assertCaTaxWithholdingsValid(taxWithholdings, context) {
    const { maxDidWhts } = context.polymeshApi.consts.corporateAction;
    const maxWithholdingEntries = conversion_1.u64ToBigNumber(maxDidWhts);
    if (maxWithholdingEntries.lt(taxWithholdings.length)) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.ValidationError,
            message: 'Too many tax withholding entries',
            data: {
                maxWithholdingEntries,
            },
        });
    }
}
exports.assertCaTaxWithholdingsValid = assertCaTaxWithholdingsValid;
/**
 * @hidden
 */
function assertCaCheckpointValid(checkpoint) {
    return __awaiter(this, void 0, void 0, function* () {
        if (checkpoint instanceof Date) {
            if (checkpoint <= new Date()) {
                throw new internal_1.PolymeshError({
                    code: types_1.ErrorCode.ValidationError,
                    message: 'Checkpoint date must be in the future',
                });
            }
        }
        else {
            const exists = yield checkpoint.exists();
            if (!exists) {
                throw new internal_1.PolymeshError({
                    code: types_1.ErrorCode.DataUnavailable,
                    message: checkpoint instanceof internal_1.Checkpoint
                        ? "Checkpoint doesn't exist"
                        : "Checkpoint Schedule doesn't exist",
                });
            }
        }
    });
}
exports.assertCaCheckpointValid = assertCaCheckpointValid;
/**
 * @hidden
 */
function assertDistributionDatesValid(checkpoint, paymentDate, expiryDate) {
    return __awaiter(this, void 0, void 0, function* () {
        let checkpointDate;
        if (checkpoint instanceof Date) {
            checkpointDate = checkpoint;
        }
        else {
            ({ nextCheckpointDate: checkpointDate } = yield checkpoint.details());
        }
        if (paymentDate <= checkpointDate) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.ValidationError,
                message: 'Payment date must be after the Checkpoint date',
            });
        }
        if (expiryDate && expiryDate < checkpointDate) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.ValidationError,
                message: 'Expiry date must be after the Checkpoint date',
            });
        }
    });
}
exports.assertDistributionDatesValid = assertDistributionDatesValid;
/**
 * @hidden
 */
function isFullGroupType(group) {
    return group instanceof internal_1.KnownPermissionGroup && group.type === types_1.PermissionGroupType.Full;
}
exports.isFullGroupType = isFullGroupType;
/**
 * @hidden
 *
 * @note based on the complexity calculation done by the chain
 * @note conditions have already been "injected" with the default trusted claim issuers when they reach this point
 */
function assertRequirementsNotTooComplex(conditions, defaultClaimIssuerLength, context) {
    const { maxConditionComplexity: maxComplexity } = context.polymeshApi.consts.complianceManager;
    let complexitySum = 0;
    conditions.forEach(condition => {
        const { target, trustedClaimIssuers = [] } = condition;
        switch (condition.type) {
            case types_1.ConditionType.IsPresent:
            case types_1.ConditionType.IsIdentity:
            case types_1.ConditionType.IsAbsent:
                // single claim conditions add one to the complexity
                complexitySum += 1;
                break;
            case types_1.ConditionType.IsAnyOf:
            case types_1.ConditionType.IsNoneOf:
                // multi claim conditions add one to the complexity per each claim
                complexitySum += condition.claims.length;
                break;
        }
        // if the condition affects both, it actually represents two conditions on chain
        if (target === types_1.ConditionTarget.Both) {
            complexitySum = complexitySum * 2;
        }
        const claimIssuerLength = trustedClaimIssuers.length || defaultClaimIssuerLength;
        complexitySum = complexitySum * claimIssuerLength;
    });
    if (conversion_1.u32ToBigNumber(maxComplexity).lt(complexitySum)) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.LimitExceeded,
            message: 'Compliance Requirement complexity limit exceeded',
            data: { limit: maxComplexity },
        });
    }
}
exports.assertRequirementsNotTooComplex = assertRequirementsNotTooComplex;
/**
 * @hidden
 */
function assertAuthorizationRequestValid(authRequest, context) {
    return __awaiter(this, void 0, void 0, function* () {
        const exists = yield authRequest.exists();
        if (!exists) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'The Authorization Request no longer exists',
            });
        }
        if (authRequest.isExpired()) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'The Authorization Request has expired',
                data: {
                    expiry: authRequest.expiry,
                },
            });
        }
        const { data } = authRequest;
        switch (data.type) {
            case types_1.AuthorizationType.RotatePrimaryKey:
                return assertPrimaryKeyRotationAuthorizationValid(authRequest);
            case types_1.AuthorizationType.AttestPrimaryKeyRotation:
                return assertAttestPrimaryKeyAuthorizationValid(authRequest);
            case types_1.AuthorizationType.TransferTicker:
                return assertTransferTickerAuthorizationValid(data, context);
            case types_1.AuthorizationType.TransferAssetOwnership:
                return assertTransferAssetOwnershipAuthorizationValid(data, context);
            case types_1.AuthorizationType.BecomeAgent:
                // no additional checks
                return;
            case types_1.AuthorizationType.AddMultiSigSigner:
                // no additional checks
                return;
            case types_1.AuthorizationType.PortfolioCustody:
                // no additional checks
                return;
            case types_1.AuthorizationType.JoinIdentity:
                return assertJoinIdentityAuthorizationValid(authRequest);
            case types_1.AuthorizationType.AddRelayerPayingKey:
                return assertAddRelayerPayingKeyAuthorizationValid(data);
            default:
                throw new UnreachableCaseError(data); // ensures switch statement covers all values
        }
    });
}
exports.assertAuthorizationRequestValid = assertAuthorizationRequestValid;
/**
 * @hidden
 *
 * Asserts valid  primary key rotation authorization
 */
function assertPrimaryKeyRotationAuthorizationValid(authRequest) {
    return __awaiter(this, void 0, void 0, function* () {
        if (authRequest.target instanceof internal_1.Identity) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.ValidationError,
                message: 'An Identity can not become the primary key of another Identity',
            });
        }
    });
}
exports.assertPrimaryKeyRotationAuthorizationValid = assertPrimaryKeyRotationAuthorizationValid;
/**
 * @hidden
 *
 * Asserts valid attest primary key authorization
 */
function assertAttestPrimaryKeyAuthorizationValid(authRequest) {
    return __awaiter(this, void 0, void 0, function* () {
        const isCddProvider = yield authRequest.issuer.isCddProvider();
        if (!isCddProvider) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'Issuer must be a CDD provider',
            });
        }
    });
}
exports.assertAttestPrimaryKeyAuthorizationValid = assertAttestPrimaryKeyAuthorizationValid;
/**
 * @hidden
 *
 * Asserts transfer ticker authorization is valid
 */
function assertTransferTickerAuthorizationValid(data, context) {
    return __awaiter(this, void 0, void 0, function* () {
        const reservation = new internal_1.TickerReservation({ ticker: data.value }, context);
        const { status } = yield reservation.details();
        if (status === types_1.TickerReservationStatus.Free) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'The Ticker is not reserved',
            });
        }
        if (status === types_1.TickerReservationStatus.TokenCreated) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'The Ticker has already been used to create an Asset',
            });
        }
    });
}
exports.assertTransferTickerAuthorizationValid = assertTransferTickerAuthorizationValid;
/**
 * @hidden
 *
 * Asserts valid transfer asset ownership authorization
 */
function assertTransferAssetOwnershipAuthorizationValid(data, context) {
    return __awaiter(this, void 0, void 0, function* () {
        const token = new internal_1.SecurityToken({ ticker: data.value }, context);
        const exists = yield token.exists();
        if (!exists)
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'The Asset does not exist',
            });
    });
}
exports.assertTransferAssetOwnershipAuthorizationValid = assertTransferAssetOwnershipAuthorizationValid;
/**
 * @hidden
 *
 * Asserts valid join identity authorization request
 */
function assertJoinIdentityAuthorizationValid(authRequest) {
    return __awaiter(this, void 0, void 0, function* () {
        const issuer = authRequest.issuer;
        const target = authRequest.target;
        const hasValidCdd = yield issuer.hasValidCdd();
        if (!hasValidCdd) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'Issuing Identity does not have a valid CDD claim',
            });
        }
        assertIsAccount(target);
        const targetIdentity = yield target.getIdentity();
        if (targetIdentity) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'The target Account already has an associated Identity',
            });
        }
    });
}
exports.assertJoinIdentityAuthorizationValid = assertJoinIdentityAuthorizationValid;
/**
 * @hidden
 *
 * Asserts valid add relayer paying key authorization
 */
function assertAddRelayerPayingKeyAuthorizationValid(data) {
    return __awaiter(this, void 0, void 0, function* () {
        const subsidy = data.value;
        const [beneficiaryIdentity, subsidizerIdentity] = yield Promise.all([
            subsidy.beneficiary.getIdentity(),
            subsidy.subsidizer.getIdentity(),
        ]);
        if (!beneficiaryIdentity) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'Beneficiary Account does not have an Identity',
            });
        }
        if (!subsidizerIdentity) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'Subsidizer Account does not have an Identity',
            });
        }
        const [isBeneficiaryCddValid, isSubsidizerCddValid] = yield Promise.all([
            beneficiaryIdentity.hasValidCdd(),
            subsidizerIdentity.hasValidCdd(),
        ]);
        if (!isBeneficiaryCddValid) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'Beneficiary Account does not have a valid CDD Claim',
            });
        }
        if (!isSubsidizerCddValid) {
            throw new internal_1.PolymeshError({
                code: types_1.ErrorCode.UnmetPrerequisite,
                message: 'Subsidizer Account does not have a valid CDD Claim',
            });
        }
    });
}
exports.assertAddRelayerPayingKeyAuthorizationValid = assertAddRelayerPayingKeyAuthorizationValid;
/**
 * @hidden
 *
 * Assert the target is an Account
 */
function assertIsAccount(target) {
    if (target instanceof internal_1.Identity) {
        throw new internal_1.PolymeshError({
            code: types_1.ErrorCode.UnmetPrerequisite,
            message: 'The target cannot be an Identity',
        });
    }
}
const createAuthorizationResolver = (auth, issuer, target, expiry, context) => (receipt) => {
    const [{ data }] = internal_2.filterEventRecords(receipt, 'identity', 'AuthorizationAdded');
    let rawAuth;
    if (auth instanceof internal_1.PostTransactionValue) {
        rawAuth = auth.value;
    }
    else {
        rawAuth = auth;
    }
    const authId = conversion_1.u64ToBigNumber(data[3]);
    return new internal_1.AuthorizationRequest({ authId, expiry, issuer, target, data: rawAuth }, context);
};
exports.createAuthorizationResolver = createAuthorizationResolver;
/**
 * @hidden
 *
 * Helper class to ensure a code path is unreachable. For example this can be used for ensuring switch statements are exhaustive
 */
class UnreachableCaseError extends Error {
    /** This should never be called */
    constructor(val) {
        super(`Unreachable case: ${JSON.stringify(val)}`);
    }
}
exports.UnreachableCaseError = UnreachableCaseError;
//# sourceMappingURL=utils.js.map