"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.baseOfferMetadataSchema = exports.baseOfferDataSchema = exports.InvalidOfferMetadataError = exports.InvalidOfferDataError = void 0;
exports.prepareRenderingData = prepareRenderingData;
exports.renderContractualAgreement = renderContractualAgreement;
exports.renderContractualAgreementForOffer = renderContractualAgreementForOffer;
const yup = __importStar(require("yup"));
const bignumber_1 = require("@ethersproject/bignumber");
const common_1 = require("@bosonprotocol/common");
const mustache_1 = __importDefault(require("mustache"));
const units_1 = require("@ethersproject/units");
const subgraph_1 = require("../subgraph");
const constants_1 = require("@ethersproject/constants");
class InvalidOfferDataError extends Error {
    constructor(missingProperties) {
        super(`InvalidOfferData - missing properties: [${missingProperties.join(",")}]`);
        this.missingProperties = missingProperties;
    }
}
exports.InvalidOfferDataError = InvalidOfferDataError;
class InvalidOfferMetadataError extends Error {
    constructor(missingProperties) {
        super(`InvalidOfferMetadata - missing properties: [${missingProperties.join(",")}]`);
        this.missingProperties = missingProperties;
    }
}
exports.InvalidOfferMetadataError = InvalidOfferMetadataError;
exports.baseOfferDataSchema = yup
    .object({
    price: yup.mixed().required(),
    sellerDeposit: yup.mixed().required(),
    agentId: yup.mixed().required(),
    buyerCancelPenalty: yup.mixed().required(),
    quantityAvailable: yup.mixed().required(),
    validFromDateInMS: yup.mixed().required(),
    validUntilDateInMS: yup.mixed().required(),
    voucherRedeemableFromDateInMS: yup.mixed().required(),
    voucherRedeemableUntilDateInMS: yup.mixed().required(),
    disputePeriodDurationInMS: yup.mixed().required(),
    resolutionPeriodDurationInMS: yup.mixed().required(),
    exchangeToken: yup.string().required(),
    disputeResolverId: yup.mixed().required(),
    metadataUri: yup.string().required(),
    metadataHash: yup.string().required()
})
    .required();
exports.baseOfferMetadataSchema = yup
    .object({
    sellerContactMethod: yup.string().required(),
    disputeResolverContactMethod: yup.string().required(),
    escalationDeposit: yup.mixed().required(),
    escalationResponsePeriodInSec: yup.mixed().required(),
    sellerTradingName: yup.string().required(),
    returnPeriodInDays: yup.number().required()
})
    .required();
function checkOfferDataIsValid(offerData, throwIfInvalid = false) {
    if (offerData === undefined || offerData === null) {
        throw new Error("InvalidOfferData - undefined");
    }
    if (typeof offerData !== "object") {
        throw new Error("InvalidOfferData - expecting an object");
    }
    try {
        exports.baseOfferDataSchema.validateSync(offerData, { abortEarly: false });
    }
    catch (e) {
        const missingProperties = [];
        const getMissingProp = (error) => {
            return error.match(/(.*) is a required field/)[1] || error;
        };
        if (throwIfInvalid) {
            if (e.errors) {
                e.errors.forEach((error) => {
                    missingProperties.push(getMissingProp(error));
                });
            }
            else {
                missingProperties.push(getMissingProp(e));
            }
            throw new InvalidOfferDataError(missingProperties);
        }
        return false;
    }
    return true;
}
function checkOfferMetadataIsValid(offerMetadata, throwIfInvalid = false) {
    if (offerMetadata === undefined || offerMetadata === null) {
        throw new Error("InvalidOfferMetadata - undefined");
    }
    if (typeof offerMetadata !== "object") {
        throw new Error("InvalidOfferMetadata - expecting an object");
    }
    try {
        exports.baseOfferMetadataSchema.validateSync(offerMetadata, { abortEarly: false });
    }
    catch (e) {
        const missingProperties = [];
        const getMissingProp = (error) => {
            return error.match(/(.*) is a required field/)[1] || error;
        };
        if (throwIfInvalid) {
            if (e.errors) {
                e.errors.forEach((error) => {
                    missingProperties.push(getMissingProp(error));
                });
            }
            else {
                missingProperties.push(getMissingProp(e));
            }
            throw new InvalidOfferMetadataError(missingProperties);
        }
        return false;
    }
    return true;
}
function convertExistingOfferData(offerDataSubGraph) {
    var _a, _b, _c, _d, _e, _f, _g;
    const productItemMetadata = ((_a = offerDataSubGraph.metadata) === null || _a === void 0 ? void 0 : _a.type) === subgraph_1.MetadataType.BUNDLE
        ? (_b = offerDataSubGraph.metadata.items) === null || _b === void 0 ? void 0 : _b.find((item) => item.type === subgraph_1.ItemMetadataType.ITEM_PRODUCT_V1)
        : undefined;
    return {
        offerData: Object.assign(Object.assign({}, offerDataSubGraph), { collectionIndex: offerDataSubGraph.collectionIndex || 0, validFromDateInMS: offerDataSubGraph.validFromDate, validUntilDateInMS: offerDataSubGraph.validUntilDate, voucherRedeemableFromDateInMS: offerDataSubGraph.voucherRedeemableFromDate, voucherRedeemableUntilDateInMS: offerDataSubGraph.voucherRedeemableUntilDate, voucherValidDurationInMS: offerDataSubGraph.voucherValidDuration, disputePeriodDurationInMS: offerDataSubGraph.disputePeriodDuration, resolutionPeriodDurationInMS: offerDataSubGraph.resolutionPeriodDuration, exchangeToken: offerDataSubGraph.exchangeToken.address, feeLimit: offerDataSubGraph.price, priceType: common_1.PriceType.Static, royaltyInfo: {
                recipients: [constants_1.AddressZero], // AddressZero means Seller's treasury account
                bps: ["0"] // Values should be greater or equal than Seller's minimum royalty amount
            } }),
        offerMetadata: {
            sellerContactMethod: (_c = offerDataSubGraph.metadata) === null || _c === void 0 ? void 0 : _c.exchangePolicy.sellerContactMethod,
            disputeResolverContactMethod: (_d = (productItemMetadata ||
                offerDataSubGraph.metadata)) === null || _d === void 0 ? void 0 : _d.exchangePolicy.disputeResolverContactMethod,
            escalationDeposit: offerDataSubGraph.disputeResolutionTerms.buyerEscalationDeposit,
            escalationResponsePeriodInSec: offerDataSubGraph.disputeResolutionTerms.escalationResponsePeriod,
            sellerTradingName: (_f = (_e = (productItemMetadata ||
                offerDataSubGraph.metadata)) === null || _e === void 0 ? void 0 : _e.productV1Seller) === null || _f === void 0 ? void 0 : _f.name,
            returnPeriodInDays: (_g = (productItemMetadata ||
                offerDataSubGraph.metadata)) === null || _g === void 0 ? void 0 : _g.shipping.returnPeriodInDays
        },
        tokenInfo: Object.assign(Object.assign({}, offerDataSubGraph.exchangeToken), { decimals: parseInt(offerDataSubGraph.exchangeToken.decimals) })
    };
}
const MAX_AND_MIN_DATE_TIMESTAMP = 8.64e15; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
function prepareRenderingData(offerData, offerMetadata, tokenInfo) {
    return __awaiter(this, void 0, void 0, function* () {
        return Object.assign(Object.assign(Object.assign({}, offerData), offerMetadata), { priceValue: (0, units_1.formatUnits)(offerData.price, tokenInfo.decimals), exchangeTokenSymbol: tokenInfo.symbol, sellerDepositValue: (0, units_1.formatUnits)(offerData.sellerDeposit, tokenInfo.decimals), buyerCancelPenaltyValue: (0, units_1.formatUnits)(offerData.buyerCancelPenalty, tokenInfo.decimals), escalationDepositValue: (0, units_1.formatUnits)(offerMetadata.escalationDeposit, tokenInfo.decimals), hasExpirationDate: !!offerData.validUntilDateInMS &&
                Number(offerData.validUntilDateInMS.toString()) <
                    MAX_AND_MIN_DATE_TIMESTAMP, toISOString: function () {
                return function (num, render) {
                    try {
                        return new Date(parseInt(render(num))).toISOString();
                    }
                    catch (_a) {
                        return `[[${num} - invalid Date]]`;
                    }
                };
            }, msecToDay: function () {
                return function (num, render) {
                    try {
                        return bignumber_1.BigNumber.from(render(num))
                            .div(common_1.utils.timestamp.MSEC_PER_DAY)
                            .toString();
                    }
                    catch (_a) {
                        return `[[${num} - invalid BigNumber]]`;
                    }
                };
            }, secToDay: function () {
                return function (num, render) {
                    try {
                        return bignumber_1.BigNumber.from(render(num))
                            .div(common_1.utils.timestamp.SEC_PER_DAY)
                            .toString();
                    }
                    catch (_a) {
                        return `[[${num} - invalid BigNumber]]`;
                    }
                };
            } });
    });
}
// inject a template + all offer details
function renderContractualAgreement(template, offerData, offerMetadata, tokenInfo) {
    return __awaiter(this, void 0, void 0, function* () {
        // Check the passed offerData is matching the required type
        checkOfferDataIsValid(offerData, true);
        checkOfferMetadataIsValid(offerMetadata, true);
        const preparedData = yield prepareRenderingData(offerData, offerMetadata, tokenInfo);
        return mustache_1.default.render(template, preparedData);
    });
}
function renderContractualAgreementForOffer(existingOfferData) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!existingOfferData) {
            throw new Error(`offerData is undefined`);
        }
        if (!existingOfferData.metadata) {
            throw new Error(`Offer Metadata is undefined`);
        }
        if (existingOfferData.metadata.type !== subgraph_1.MetadataType.PRODUCT_V1) {
            throw new Error(`Invalid Offer Metadata: Type is not supported: '${existingOfferData.metadata.type}'`);
        }
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        if (!existingOfferData.metadata.exchangePolicy) {
            throw new Error(`Invalid Offer Metadata: exchangePolicy is not defined`);
        }
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        if (!existingOfferData.metadata.exchangePolicy.template) {
            throw new Error(`Invalid Offer Metadata: exchangePolicy.template is not defined`);
        }
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const template = existingOfferData.metadata.exchangePolicy.template;
        const convertedOfferArgs = convertExistingOfferData(existingOfferData);
        return renderContractualAgreement(template, convertedOfferArgs.offerData, convertedOfferArgs.offerMetadata, convertedOfferArgs.tokenInfo);
    });
}
//# sourceMappingURL=renderContractualAgreement.js.map