"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolvePath = resolvePath;
exports.disallowAdditionalProps = disallowAdditionalProps;
const deno_js_1 = __importDefault(require("../../deno.js"));
const errors_js_1 = require("./errors.js");
const index_js_1 = __importDefault(require("./validate/index.js"));
const consts_js_1 = require("../consts.js");
const versions_js_1 = require("./versions.js");
function resolvePath(obj, instancePath) {
    const path = instancePath.split("/");
    let ref = obj;
    for (let i = 1; i < path.length; i++) {
        ref = ref[path[i]];
    }
    return ref;
}
const doneAlready = new Map();
function disallowAdditionalProps(definitions, show = false) {
    if (doneAlready.has(definitions))
        return;
    doneAlready.set(definitions, true);
    const disallowed = new Set();
    for (const key of Object.keys(definitions)) {
        if (key.match(/Options$/)) {
            continue;
        }
        const def = definitions[key];
        if (def.type === "object") {
            if (def.additionalProperties === undefined ||
                typeof def.additionalProperties === "object" &&
                    Object.keys(def.additionalProperties).length === 0) {
                def.additionalProperties = false;
                disallowed.add(key);
            }
        }
    }
    /* istanbul ignore next */
    if (show) {
        console.log("Disallowed additional props in " + Array.from(disallowed).join(", "));
    }
}
function aggregateErrors(inputErrors) {
    const missingMap = new Map();
    const additionalMap = new Map();
    const errors = inputErrors.filter((error) => {
        if (error.subErrors) {
            error.subErrors = aggregateErrors(error.subErrors);
        }
        if (error.schemaPath) {
            const key = error.schemaPath + "|" + error.instancePath;
            if (error.message === "Missing required property") {
                let arr;
                if (missingMap.has(key)) {
                    arr = missingMap.get(key);
                }
                else {
                    arr = [];
                    missingMap.set(key, arr);
                }
                arr.push(error);
                return false;
            }
            else if (error.message === "should NOT have additional properties") {
                let arr;
                if (additionalMap.has(key)) {
                    arr = additionalMap.get(key);
                }
                else {
                    arr = [];
                    additionalMap.set(key, arr);
                }
                arr.push(error);
                return false;
            }
        }
        return true;
    });
    for (const arr of missingMap.values()) {
        const missing = [];
        for (const error of arr) {
            missing.push(error.data || "somethingWentWrong(tm)");
        }
        errors.push({
            schemaPath: arr[0].schemaPath,
            instancePath: arr[0].instancePath,
            message: "Missing required properties",
            params: { missing },
        });
    }
    for (const arr of additionalMap.values()) {
        const additionalProperties = {};
        for (const error of arr) {
            const additionalProperty = error.params?.additionalProperty ||
                "somethingWentWrong(tm)";
            additionalProperties[additionalProperty] = error.data[additionalProperty];
        }
        errors.push({
            schemaPath: arr[0].schemaPath,
            instancePath: arr[0].instancePath,
            message: "should NOT have additional properties",
            params: { additionalProperties },
        });
    }
    return errors;
}
function validate({ source, type, object, schemaOrSchemaKey, definitions, options, logger, logObj, versionCheck, }) {
    const _errors = (0, index_js_1.default)(object, schemaOrSchemaKey, { definitions, logger, logObj });
    // if (_errors === false || !_errors.length) return;
    if (_errors.length === 0)
        return;
    const errors = aggregateErrors(_errors);
    if (type === "result") {
        /* istanbul ignore else */
        /*
        if (errors) {
          let origData: any = false;
    
          errors.forEach((error) => {
            // For now let's ignore the base object which could be huge.
            /* istanbul ignore else */
        /*
            if (error.instancePath !== "")
              // Note, not the regular ajv data value from verbose:true
              error.data = resolvePath(object, error.instancePath);
    
            if (error.schemaPath === "#/anyOf") {
              if (origData === false) {
                origData = error.data;
              } else if (origData === error.data) {
                error.data = "[shortened by validateAndCoerceTypes]";
              }
            }
          });
    
          // Becaue of the "anyOf" in quote, errors are huuuuge and mostly
          // irrelevant... so let's filter out (some of) the latter
          errors = errors.filter((error) => {
            if (error.schemaPath.startsWith("#/definitions/Quote")) {
              const schemaQuoteType = error.schemaPath
                .substring(19)
                .split("/")[0]
                .toUpperCase();
    
              /*
               * Filter out entries for non-matching schema type, i.e.
               * {
               *   schemaPath: '#/definitions/QuoteCryptoCurrency/properties...'
               *   data: {
               *     quoteType: "EQUITY"
               *   }
               * }
               */
        /*
              if (
                typeof error.data === "object" &&
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore: Properrty "quoteType" does not exist on type "object"
                error.data?.quoteType !== schemaQuoteType
              )
                return false;
    
              /*
               * Filter out the non-matching "const" for the above.
               * {
               *   schemaPath: '#/definitions/QuoteCryptoCurrency/properties/quoteType/const'
               *   keyword: "const",
               *   params: { allowedValue: "CRYPTOCURRENCY"}},
               *   data: "EQUITY"
               * }
               */
        /*
              if (
                typeof error.data === "string" &&
                error.params?.allowedValue === schemaQuoteType
              )
                return false;
            }
            return true;
          });
    
          // In the case of there being NO match in #anyOf, bring back the data
          if (errors.length === 1 && errors[0].schemaPath === "#/anyOf")
            errors[0].data = origData;
        }
        */
        if (options.logErrors === true) {
            const title = encodeURIComponent("Failed validation: " + schemaOrSchemaKey);
            logger.error("The following result did not validate with schema: " +
                schemaOrSchemaKey);
            logObj(errors, { depth: 5 });
            // logObj(object);
            logger.error(`
This may happen intermittently and you should catch errors appropriately.  However:  1) if this recently started happening on every request for a symbol that used to work, Yahoo may have changed their API.  2) If this happens on every request for a symbol you've never used before, but not for other symbols, you've found an edge-case (OR, we may just be protecting you from "bad" data sometimes stored for e.g. misspelt symbols on Yahoo's side).

Please see if anyone has reported this previously:

  ${consts_js_1.repository}/issues?q=is%3Aissue+${title}

or open a new issue (and mention the symbol):  ${deno_js_1.default.name} v${deno_js_1.default.version}

  ${consts_js_1.repository}/issues/new?labels=bug%2C+validation&template=validation.md&title=${title}

For information on how to turn off the above logging or skip these errors, see https://github.com/gadicc/yahoo-finance2/tree/devel/docs/validation.md.

At the end of the doc, there's also a section on how to "Help Fix Validation Errors" in case you'd like to contribute to the project.  Most of the time, these fixes are very quick and easy; it's just hard for our small core team to keep up, so help is always appreciated!
`);
            if (versionCheck) {
                (0, versions_js_1.versionCheck)().then((result) => {
                    if (!result.isLatest) {
                        logger.info(`Additionally, your yahoo-finance2 version out of date: ${result.current} < ${result.latest} (latest)`);
                    }
                }).catch((error) => {
                    logger.error(`Failed to check version: ${error.message}`);
                });
            }
        } /* if (logErrors) */
        throw new errors_js_1.FailedYahooValidationError("Failed Yahoo Schema validation", {
            result: object,
            errors: errors,
        });
    } /* if (type === 'options') */
    else {
        if (options.logOptionsErrors === true) {
            logger.error(`[yahooFinance.${source}] Invalid options ("${schemaOrSchemaKey}")`);
            logObj({ errors, input: object });
        }
        throw new errors_js_1.InvalidOptionsError(`yahooFinance.${source} called with invalid options.`);
    }
}
exports.default = validate;
