'use strict';

/*!
 * ignore
 */

var Mixed = require('../../schema/mixed');
var mpath = require('mpath');

/*!
 * @param {Schema} schema
 * @param {Object} doc POJO
 * @param {string} path
 */

module.exports = function getSchemaTypes(schema, doc, path) {
  var pathschema = schema.path(path);

  if (pathschema) {
    return pathschema;
  }

  function search(parts, schema) {
    var p = parts.length + 1;
    var foundschema;
    var trypath;

    while (p--) {
      trypath = parts.slice(0, p).join('.');
      foundschema = schema.path(trypath);
      if (foundschema) {
        if (foundschema.caster) {
          // array of Mixed?
          if (foundschema.caster instanceof Mixed) {
            return foundschema.caster;
          }

          var schemas = null;
          if (doc != null && foundschema.schema != null && foundschema.schema.discriminators != null) {
            var discriminators = foundschema.schema.discriminators;
            var keys = mpath.get(trypath + '.' + foundschema.schema.options.discriminatorKey,
              doc);
            schemas = Object.keys(discriminators).
              reduce(function(cur, discriminator) {
                if (keys.indexOf(discriminator) !== -1) {
                  cur.push(discriminators[discriminator]);
                }
                return cur;
              }, []);
          }

          // Now that we found the array, we need to check if there
          // are remaining document paths to look up for casting.
          // Also we need to handle array.$.path since schema.path
          // doesn't work for that.
          // If there is no foundschema.schema we are dealing with
          // a path like array.$
          if (p !== parts.length && foundschema.schema) {
            var ret;
            if (parts[p] === '$') {
              if (p + 1 === parts.length) {
                // comments.$
                return foundschema;
              }
              // comments.$.comments.$.title
              ret = search(parts.slice(p + 1), schema);
              if (ret) {
                ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
                  !foundschema.schema.$isSingleNested;
              }
              return ret;
            }

            if (schemas != null && schemas.length > 0) {
              ret = [];
              for (var i = 0; i < schemas.length; ++i) {
                var _ret = search(parts.slice(p), schemas[i]);
                if (_ret != null) {
                  _ret.$isUnderneathDocArray = _ret.$isUnderneathDocArray ||
                    !foundschema.schema.$isSingleNested;
                  if (_ret.$isUnderneathDocArray) {
                    ret.$isUnderneathDocArray = true;
                  }
                  ret.push(_ret);
                }
              }
              return ret;
            } else {
              ret = search(parts.slice(p), foundschema.schema);

              if (ret) {
                ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
                  !foundschema.schema.$isSingleNested;
              }

              return ret;
            }
          }
        }

        return foundschema;
      }
    }
  }

  // look for arrays
  var parts = path.split('.');
  for (var i = 0; i < parts.length; ++i) {
    if (parts[i] === '$') {
      // Re: gh-5628, because `schema.path()` doesn't take $ into account.
      parts[i] = '0';
    }
  }
  return search(parts, schema);
};
