"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
/* PROMISE_DEF */
const BluebirdPromise = require("bluebird");
/* END_PROMISE_DEF */
const log_1 = require("../log/log");
const ValidationException_1 = require("./ValidationException");
const ValidationContext_1 = require("./ValidationContext");
const ImposedRules_1 = require("../rules/ImposedRules");
class Validator {
    constructor(api) {
        this.proggressCallback = (msg, proggress) => { };
        this.concurrency = 4;
        this.providedRulesets = undefined;
        this.selectRuleset = (rulesets, voteorder) => {
            for (let i = 0; i < rulesets.rulesets.length; i++) {
                const ruleset = rulesets.rulesets[i];
                if (ruleset.name === voteorder.rulesetName) {
                    log_1.Log.log().cheapDebug(() => "VALIDATOR_SELECTED_RULESET=" + JSON.stringify(ruleset.rules));
                    return ruleset.rules;
                }
            }
            throw new ValidationException_1.ValidationException("Delegator had no such ruleset (name=" + voteorder.rulesetName + ") at specified datetime.");
        };
        this.api = api;
    }
    withConcurrency(concurrency) {
        this.concurrency = concurrency;
        return this;
    }
    withProggressCallback(proggressCallback) {
        this.proggressCallback = proggressCallback;
        return this;
    }
    provideRulesets(rulesets) {
        log_1.Log.log().cheapDebug(() => "VALIDATOR_PROVIDED_WITH_RULESETS=" + JSON.stringify(rulesets));
        this.providedRulesets = rulesets;
    }
    validate(delegator, voter, voteorder, atMoment) {
        return __awaiter(this, void 0, void 0, function* () {
            log_1.Log.log().cheapDebug(() => "VALIDATOR_VALIDATE=" + JSON.stringify({ delegator: delegator, voter: voter, voteorder: voteorder, atMoment: atMoment }));
            try {
                const context = new ValidationContext_1.ValidationContext(this.api, delegator, voter, voteorder);
                this.validateVoteorderObject(voteorder);
                this.proggressCallback("Loading rulesets...", 0);
                let rulesets;
                if (this.providedRulesets) {
                    rulesets = this.providedRulesets;
                }
                else {
                    const esrArray = yield this.api.loadRulesets({ delegator: delegator, voter: voter }, atMoment);
                    rulesets = (esrArray.length > 0 ? esrArray[0] : { rulesets: [] });
                }
                log_1.Log.log().cheapDebug(() => "VALIDATOR_USING_RULESETS=" + JSON.stringify(rulesets));
                let rules = this.selectRuleset(rulesets, voteorder); // select correct ruleset (using rulesetName)
                rules = rules.concat(ImposedRules_1.ImposedRules.getImposedRules(delegator, voter)); // apply imposed rules (this rules are necessary to prevent violations of some of the steem blockchain rules)
                this.proggressCallback("Validating rules...", 0.3);
                yield this.validateRules(rules, voteorder, context);
                this.proggressCallback("Validation done", 1.0);
                return true;
            }
            catch (error) {
                if (ValidationException_1.ValidationException.isValidationException(error))
                    return error;
                else
                    throw error;
            }
        });
    }
    validateVoteorderObject(voteorder) {
        if (typeof voteorder === "undefined")
            throw new ValidationException_1.ValidationException("Voteorder must not be empty");
        if (typeof voteorder.rulesetName === "undefined" || voteorder.rulesetName.length == 0)
            throw new ValidationException_1.ValidationException("Ruleset_name must not be empty");
        if (typeof voteorder.author === "undefined" || voteorder.author.length == 0)
            throw new ValidationException_1.ValidationException("Author must not be empty");
        if (typeof voteorder.permlink === "undefined" || voteorder.permlink.length == 0)
            throw new ValidationException_1.ValidationException("Permlink must not be empty");
        if (typeof voteorder.weight === "undefined" || isNaN(voteorder.weight))
            throw new ValidationException_1.ValidationException("Weight must not be empty");
        if (voteorder.weight < -10000)
            throw new ValidationException_1.ValidationException("Weight must be greater or equal -10000");
        if (voteorder.weight > 10000)
            throw new ValidationException_1.ValidationException("Weight must be lesser or equal 10000");
    }
    validateRules(rules, voteorder, context) {
        return __awaiter(this, void 0, void 0, function* () {
            yield BluebirdPromise.resolve(rules)
                .map((rule) => __awaiter(this, void 0, void 0, function* () {
                try {
                    yield rule.validate(voteorder, context);
                    log_1.Log.log().cheapDebug(() => "VALIDATOR_RULE_VALIDATION_SUCCEEDED=" + JSON.stringify(rule));
                }
                catch (error) {
                    log_1.Log.log().cheapDebug(() => "VALIDATOR_RULE_VALIDATION_FAILED=" + JSON.stringify({ rule: rule, error: error.message }));
                    throw error;
                }
            }), { concurrency: this.concurrency });
        });
    }
}
exports.Validator = Validator;
//# sourceMappingURL=Validator.js.map