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 __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
function debounce(f, wait) {
    if (wait === void 0) { wait = 200; }
    var timeout = undefined;
    return function () {
        var context = this;
        var args = arguments;
        window.clearTimeout(timeout);
        timeout = window.setTimeout(function () { return f.apply(context, args); }, wait);
    };
}
var isEmail = function (value) {
    var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(value);
};
var Validator = /** @class */ (function () {
    function Validator() {
        this.rules = new Map();
    }
    Validator.prototype.validate = function (validation, el, meta) {
        if (meta === void 0) { meta = {}; }
        return __awaiter(this, void 0, void 0, function () {
            var value, metadata, rules, errors, _i, rules_1, _a, rule, args, error;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        value = el.value;
                        metadata = Object.assign(Object.assign({}, meta), { el: el, tag: el.tagName.toLowerCase() });
                        rules = validation
                            .replace(/\s/g, '')
                            .split('|')
                            .map(function (r) {
                            var _a = r.split(':'), rule = _a[0], argPart = _a.slice(1);
                            var arg = argPart.join(':');
                            var args = arg ? arg.split(',') : [];
                            return { rule: rule, args: args };
                        });
                        errors = [];
                        _i = 0, rules_1 = rules;
                        _b.label = 1;
                    case 1:
                        if (!(_i < rules_1.length)) return [3 /*break*/, 4];
                        _a = rules_1[_i], rule = _a.rule, args = _a.args;
                        return [4 /*yield*/, this.validateRule(rule, args, value, metadata)];
                    case 2:
                        error = _b.sent();
                        if (error)
                            errors.push(error);
                        _b.label = 3;
                    case 3:
                        _i++;
                        return [3 /*break*/, 1];
                    case 4: return [2 /*return*/, errors];
                }
            });
        });
    };
    Validator.prototype.validateRule = function (rule, args, value, meta) {
        if (args === void 0) { args = []; }
        return __awaiter(this, void 0, void 0, function () {
            var validator, result, valid, message, ctx, _a, _b;
            return __generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        validator = this.rules.get(rule);
                        if (!validator)
                            throw new Error("[" + meta.tag + "] Unknown validation rule: " + rule);
                        return [4 /*yield*/, validator(value, args, meta)];
                    case 1:
                        result = _c.sent();
                        valid = typeof result === 'boolean' ? result : result.valid;
                        message = typeof result === 'boolean' ? '' : result.message;
                        if (valid)
                            return [2 /*return*/, null];
                        ctx = { args: args, message: message, meta: meta, rule: rule, value: value };
                        _a = {
                            rule: rule
                        };
                        if (!this.translator) return [3 /*break*/, 3];
                        return [4 /*yield*/, this.translator(ctx)];
                    case 2:
                        _b = _c.sent();
                        return [3 /*break*/, 4];
                    case 3:
                        _b = message;
                        _c.label = 4;
                    case 4: return [2 /*return*/, (_a.message = _b,
                            _a.meta = meta,
                            _a)];
                }
            });
        });
    };
    return Validator;
}());
var validator = new Validator();
validator.rules.set('alpha', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_1, valid, message;
    return __generator(this, function (_a) {
        if (typeof value !== 'string') {
            message_1 = getUnsupportedMessage('alpha', meta);
            throw new Error(message_1);
        }
        valid = /^[A-Z]*$/i.test(value);
        message = 'This field should only contain alphabetic characters [A-Z].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('alpha-dash', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_2, valid, message;
    return __generator(this, function (_a) {
        if (typeof value !== 'string') {
            message_2 = getUnsupportedMessage('alpha-dash', meta);
            throw new Error(message_2);
        }
        valid = /^[0-9A-Z_-]*$/i.test(value);
        message = 'This field should only contain alphanumeric characters or dashes [0-9A-Z_-].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('alpha-num', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_3, valid, message;
    return __generator(this, function (_a) {
        if (typeof value !== 'string') {
            message_3 = getUnsupportedMessage('alpha-num', meta);
            throw new Error(message_3);
        }
        valid = /^[0-9A-Z]*$/i.test(value);
        message = 'This field should only contain alphanumeric characters [0-9A-Z].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('alpha-spaces', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_4, valid, message;
    return __generator(this, function (_a) {
        if (typeof value !== 'string') {
            message_4 = getUnsupportedMessage('alpha-spaces', meta);
            throw new Error(message_4);
        }
        valid = /^[A-Z\s]*$/i.test(value);
        message = 'This field should only contain alphabetic characters or whitespace [A-Zs].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('between', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_5, numbers, err, min, max, valid, message;
    return __generator(this, function (_a) {
        if (typeof value !== 'string' && typeof value !== 'number') {
            message_5 = getUnsupportedMessage('between', meta);
            throw new Error(message_5);
        }
        numbers = args.map(function (str) { return Number(str); }).filter(function (v) { return !isNaN(v); });
        if (numbers.length !== 2) {
            err = "[" + meta.tag + "] The 'between' validation rule requires two numeric argument (e.g. between:5,10).";
            throw new Error(err);
        }
        min = numbers[0], max = numbers[1];
        valid = min <= Number(value) && max >= Number(value);
        message = "This field should contain a value between " + min + " and " + max + ".";
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('digits', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_6, length, err, str, valid, message;
    return __generator(this, function (_a) {
        if (typeof value !== 'string' && typeof value !== 'number') {
            message_6 = getUnsupportedMessage('between', meta);
            throw new Error(message_6);
        }
        length = Number(args[0]);
        if (isNaN(length)) {
            err = "[" + meta.tag + "] The 'digits' validation rule requires a numeric argument (e.g. digits:5).";
            throw new Error(err);
        }
        str = '' + value;
        valid = /^[0-9]*$/.test(str) && str.length === length;
        message = "This field requires " + length + " digits.";
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('email', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_7, valid, message;
    return __generator(this, function (_a) {
        if (typeof value !== 'undefined' && typeof value !== 'string') {
            message_7 = getUnsupportedMessage('email', meta);
            throw new Error(message_7);
        }
        valid = !value || isEmail(value);
        message = 'This is not a valid email.';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('in', function (value, args) { return __awaiter(void 0, void 0, void 0, function () {
    var valid, message;
    return __generator(this, function (_a) {
        valid = Array.isArray(value)
            ? value.every(function (v) { return args.indexOf(v) >= 0; })
            : args.indexOf('' + value) >= 0;
        message = 'This value is not allowed.';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('is', function (value, args) { return __awaiter(void 0, void 0, void 0, function () {
    var check, valid, message;
    return __generator(this, function (_a) {
        check = args.length > 1 ? args : args[0];
        valid = Array.isArray(check)
            ? (Array.isArray(value) && !value.length) ||
                JSON.stringify(check) === JSON.stringify(value)
            : check == undefined || check === '' + value;
        message = "The value should be: \"" + check + "\".";
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('max', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_8, el, size, max, message_9, maxDate, message_10, message_11, message;
    return __generator(this, function (_a) {
        if (typeof value === 'boolean') {
            message_8 = getUnsupportedMessage('max', meta);
            throw new Error(message_8);
        }
        el = meta.el;
        size = getSize(el);
        max = getRangeArg(args[0], el);
        if (Array.isArray(value)) {
            message_9 = "This field may not exceed " + max + " options.";
            return [2 /*return*/, { valid: size <= max, message: message_9 }];
        }
        if ('type' in el && el.type === 'date') {
            maxDate = new Date(max).toISOString().slice(0, 10);
            message_10 = "This field max not not exceed " + maxDate + ".";
            return [2 /*return*/, { valid: !value || size <= max, message: message_10 }];
        }
        if (('type' in el && el.type === 'number') || typeof value === 'number') {
            message_11 = "This field may not exceed " + max + ".";
            return [2 /*return*/, { valid: value == undefined || size <= max, message: message_11 }];
        }
        message = "This field may not exceed " + max + " characters.";
        return [2 /*return*/, { valid: !value || size <= max, message: message }];
    });
}); });
validator.rules.set('min', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_12, el, size, min, message_13, minDate, message_14, message_15, message;
    return __generator(this, function (_a) {
        if (typeof value === 'boolean') {
            message_12 = getUnsupportedMessage('max', meta);
            throw new Error(message_12);
        }
        el = meta.el;
        size = getSize(el);
        min = getRangeArg(args[0], el);
        if (Array.isArray(value)) {
            message_13 = "This requires at least " + min + " options.";
            return [2 /*return*/, { valid: size === 0 || size >= min, message: message_13 }];
        }
        if ('type' in el && el.type === 'date') {
            minDate = new Date(min).toISOString().slice(0, 10);
            message_14 = "This field must be after " + minDate + ".";
            return [2 /*return*/, { valid: !value || size >= min, message: message_14 }];
        }
        if (('type' in el && el.type === 'number') || typeof value === 'number') {
            message_15 = "This field should be " + min + " or higher.";
            return [2 /*return*/, { valid: value == undefined || size >= min, message: message_15 }];
        }
        message = "This field requires at least " + min + " characters.";
        return [2 /*return*/, { valid: !value || size >= min, message: message }];
    });
}); });
validator.rules.set('numeric', function (value, args, meta) { return __awaiter(void 0, void 0, void 0, function () {
    var message_16, str, valid, message;
    return __generator(this, function (_a) {
        if (typeof value !== 'string' && typeof value !== 'number') {
            message_16 = getUnsupportedMessage('numeric', meta);
            throw new Error(message_16);
        }
        str = '' + value;
        valid = /^[0-9]+$/.test(str);
        message = 'This field should only contain numeric characters [0-9].';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
validator.rules.set('required', function (value) { return __awaiter(void 0, void 0, void 0, function () {
    var valid, message;
    return __generator(this, function (_a) {
        valid = Array.isArray(value) ? !!value.length : value === 0 || !!value;
        message = 'This field is required.';
        return [2 /*return*/, { valid: valid, message: message }];
    });
}); });
var getUnsupportedMessage = function (rule, meta) {
    return "[" + meta.el + "] Unsupported validation rule: " + rule;
};
var getSize = function (el) {
    if (Array.isArray(el.value))
        return el.value.length;
    if ('type' in el && el.type === 'date')
        return new Date(el.value || 0).getTime();
    if (('type' in el && el.type === 'number') || typeof el.value === 'number')
        return Number(el.value);
    return ('' + el.value).length;
};
var getRangeArg = function (arg, el) {
    if ('type' in el && el.type === 'date')
        return new Date(arg || 0).getTime();
    return arg == undefined ? 0 : Number(arg);
};
export { debounce as d, isEmail as i, validator as v };
