const yapi = require('../yapi.js');
const projectModel = require('../models/project.js');
const interfaceModel = require('../models/interface.js');
const mockExtra = require('../../common/mock-extra.js');
const { schemaValidator } = require('../../common/utils.js');
const _ = require('underscore');
const Mock = require('mockjs');
const variable = require('../../client/constants/variable.js')
/**
 *
 * @param {*} apiPath /user/tom
 * @param {*} apiRule /user/:username
 */
function matchApi(apiPath, apiRule) {
  let apiRules = apiRule.split('/');
  let apiPaths = apiPath.split('/');
  let pathParams = {
    __weight: 0
  };

  if (apiPaths.length !== apiRules.length) {
    return false;
  }
  for (let i = 0; i < apiRules.length; i++) {
    if (apiRules[i]) {
      apiRules[i] = apiRules[i].trim();
    } else {
      continue;
    }
    if (
      apiRules[i].length > 2 &&
      apiRules[i][0] === '{' &&
      apiRules[i][apiRules[i].length - 1] === '}'
    ) {
      pathParams[apiRules[i].substr(1, apiRules[i].length - 2)] = apiPaths[i];
    } else if (apiRules[i].indexOf(':') === 0) {
      pathParams[apiRules[i].substr(1)] = apiPaths[i];
    } else if (
      apiRules[i].length > 2 &&
      apiRules[i].indexOf('{') > -1 &&
      apiRules[i].indexOf('}') > -1
    ) {
      let params = [];
      apiRules[i] = apiRules[i].replace(/\{(.+?)\}/g, function(src, match) {
        params.push(match);
        return '([^\\/\\s]+)';
      });
      apiRules[i] = new RegExp(apiRules[i]);
      if (!apiRules[i].test(apiPaths[i])) {
        return false;
      }

      let matchs = apiPaths[i].match(apiRules[i]);

      params.forEach((item, index) => {
        pathParams[item] = matchs[index + 1];
      });
    } else {
      if (apiRules[i] !== apiPaths[i]) {
        return false;
      }else{
        pathParams.__weight++;
      }
    }
  }
  return pathParams;
}

function parseCookie(str) {
  if (!str || typeof str !== 'string') {
    return str;
  }
  if (str.split(';')[0]) {
    let c = str.split(';')[0].split('=');
    return { name: c[0], value: c[1] || '' };
  }
  return null;
}

function handleCorsRequest(ctx) {
  let header = ctx.request.header;
  ctx.set('Access-Control-Allow-Origin', header.origin);
  ctx.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, HEADER, PATCH, OPTIONS');
  ctx.set('Access-Control-Allow-Headers', header['access-control-request-headers']);
  ctx.set('Access-Control-Allow-Credentials', true);
  ctx.set('Access-Control-Max-Age', 1728000);
  ctx.body = 'ok';
}
// 必填字段是否填写好
function mockValidator(interfaceData, ctx) {
  let i,
    j,
    l,
    len,
    noRequiredArr = [];
  let method = interfaceData.method.toUpperCase() || 'GET';
  // query 判断
  for (i = 0, l = interfaceData.req_query.length; i < l; i++) {
    let curQuery = interfaceData.req_query[i];
    if (curQuery && typeof curQuery === 'object' && curQuery.required === '1') {
      if (!ctx.query[curQuery.name]) {
        noRequiredArr.push(curQuery.name);
      }
    }
  }
  // form 表单判断
  if (variable.HTTP_METHOD[method].request_body && interfaceData.req_body_type === 'form') {
    for (j = 0, len = interfaceData.req_body_form.length; j < len; j++) {
      let curForm = interfaceData.req_body_form[j];
      if (curForm && typeof curForm === 'object' && curForm.required === '1') {
        if (
          ctx.request.body[curForm.name] ||
          (ctx.request.body.fields && ctx.request.body.fields[curForm.name]) ||
          (ctx.request.body.files && ctx.request.body.files[curForm.name])
        ) {
          continue;
        }

        noRequiredArr.push(curForm.name);
      }
    }
  }
  let validResult;
  // json schema 判断
  if (variable.HTTP_METHOD[method].request_body  && interfaceData.req_body_type === 'json' && interfaceData.req_body_is_json_schema === true) {
    const schema = yapi.commons.json_parse(interfaceData.req_body_other);
    const params = yapi.commons.json_parse(ctx.request.body);
    validResult = schemaValidator(schema, params);
  }
  if (noRequiredArr.length > 0 || (validResult && !validResult.valid)) {
    let message = `错误信息：`;
    message += noRequiredArr.length > 0 ? `缺少必须字段 ${noRequiredArr.join(',')}  ` : '';
    message += validResult && !validResult.valid ? `schema 验证请求参数 ${validResult.message}` : '';

    return {
      valid: false,
      message
    };
  }

  return { valid: true };
}

module.exports = async (ctx, next) => {
  // no used variable 'hostname' & 'config'
  // let hostname = ctx.hostname;
  // let config = yapi.WEBCONFIG;
  let path = ctx.path;
  let header = ctx.request.header;

  if (path.indexOf('/mock/') !== 0) {
    if (next) await next();
    return true;
  }

  let paths = path.split('/');
  let projectId = paths[2];
  paths.splice(0, 3);
  path = '/' + paths.join('/');

  ctx.set('Access-Control-Allow-Origin', header.origin);
  ctx.set('Access-Control-Allow-Credentials', true);

  // ctx.set('Access-Control-Allow-Origin', '*');

  if (!projectId) {
    return (ctx.body = yapi.commons.resReturn(null, 400, 'projectId不能为空'));
  }

  let projectInst = yapi.getInst(projectModel),
    project;
  try {
    project = await projectInst.get(projectId);
  } catch (e) {
    return (ctx.body = yapi.commons.resReturn(null, 403, e.message));
  }

  if (!project) {
    return (ctx.body = yapi.commons.resReturn(null, 400, '不存在的项目'));
  }

  let interfaceData, newpath;
  let interfaceInst = yapi.getInst(interfaceModel);

  try {
    newpath = path.substr(project.basepath.length);
    interfaceData = await interfaceInst.getByPath(project._id, newpath, ctx.method);
    let queryPathInterfaceData = await interfaceInst.getByQueryPath(project._id, newpath, ctx.method);
    //处理query_path情况  url 中有 ?params=xxx
    if (!interfaceData || interfaceData.length != queryPathInterfaceData.length) {

      let i,
        l,
        j,
        len,
        curQuery,
        match = false;
      for (i = 0, l = queryPathInterfaceData.length; i < l; i++) {
        match = false;
        let currentInterfaceData = queryPathInterfaceData[i];
        curQuery = currentInterfaceData.query_path;
        if (!curQuery || typeof curQuery !== 'object' || !curQuery.path) {
          continue;
        }
        for (j = 0, len = curQuery.params.length; j < len; j++) {
          if (ctx.query[curQuery.params[j].name] !== curQuery.params[j].value) {
            continue;
          }
          if (j === len - 1) {
            match = true;
          }
        }

        if (match) {
          interfaceData = [currentInterfaceData];
          break;
        }
        // if (i === l - 1) {
        //   interfaceData = [];
        // }

      }
    }

    //处理动态路由
    if (!interfaceData || interfaceData.length === 0) {
      let newData = await interfaceInst.getVar(project._id, ctx.method);

      let findInterface;
      let weight = 0;
      _.each(newData, item => {
        let m = matchApi(newpath, item.path);
        if (m !== false) {
          if(m.__weight >= weight){
            findInterface = item;
          }
          delete m.__weight;
          ctx.request.query = Object.assign(m, ctx.request.query);
          return true;
        }
        return false;
      });

      if (!findInterface) {
        //非正常跨域预检请求回应
        if (ctx.method === 'OPTIONS' && ctx.request.header['access-control-request-method']) {
          return handleCorsRequest(ctx);
        }

        return (ctx.body = yapi.commons.resReturn(
          null,
          404,
          `不存在的api, 当前请求path为 ${newpath}， 请求方法为 ${
            ctx.method
          } ，请确认是否定义此请求。`
        ));
      }
      interfaceData = [await interfaceInst.get(findInterface._id)];
    }

    if (interfaceData.length > 1) {
      return (ctx.body = yapi.commons.resReturn(null, 405, '存在多个api，请检查数据库'));
    } else {
      interfaceData = interfaceData[0];
    }

    // 必填字段是否填写好
    if (project.strice) {
      const validResult = mockValidator(interfaceData, ctx);
      if (!validResult.valid) {
        return (ctx.body = yapi.commons.resReturn(
          null,
          404,
          `接口字段验证不通过, ${validResult.message}`
        ));
      }
    }

    let res;
    // mock 返回值处理
    res = interfaceData.res_body;
    try {
      if (interfaceData.res_body_type === 'json') {
        if (interfaceData.res_body_is_json_schema === true) {
          //json-schema
          const schema = yapi.commons.json_parse(interfaceData.res_body);
          res = yapi.commons.schemaToJson(schema, {
            alwaysFakeOptionals: true
          });
        } else {
          // console.log('header', ctx.request.header['content-type'].indexOf('multipart/form-data'))
          // 处理 format-data

          if (
            _.isString(ctx.request.header['content-type']) &&
            ctx.request.header['content-type'].indexOf('multipart/form-data') > -1
          ) {
            ctx.request.body = ctx.request.body.fields;
          }
          // console.log('body', ctx.request.body)

          res = mockExtra(yapi.commons.json_parse(interfaceData.res_body), {
            query: ctx.request.query,
            body: ctx.request.body,
            params: Object.assign({}, ctx.request.query, ctx.request.body)
          });
          // console.log('res',res)
        }

        try {
          res = Mock.mock(res);
        } catch (e) {
          console.log('err', e.message);
          yapi.commons.log(e, 'error');
        }
      }

      let context = {
        projectData: project,
        interfaceData: interfaceData,
        ctx: ctx,
        mockJson: res,
        resHeader: {},
        httpCode: 200,
        delay: 0
      };

      if (project.is_mock_open && project.project_mock_script) {
        // 项目层面的mock脚本解析
        let script = project.project_mock_script;
        await yapi.commons.handleMockScript(script, context);
      }

      await yapi.emitHook('mock_after', context);

      let handleMock = new Promise(resolve => {
        setTimeout(() => {
          resolve(true);
        }, context.delay);
      });
      await handleMock;
      if (context.resHeader && typeof context.resHeader === 'object') {
        for (let i in context.resHeader) {
          let cookie;
          if (i === 'Set-Cookie') {
            if (context.resHeader[i] && typeof context.resHeader[i] === 'string') {
              cookie = parseCookie(context.resHeader[i]);
              if (cookie && typeof cookie === 'object') {
                ctx.cookies.set(cookie.name, cookie.value, {
                  maxAge: 864000000,
                  httpOnly: false
                });
              }
            } else if (context.resHeader[i] && Array.isArray(context.resHeader[i])) {
              context.resHeader[i].forEach(item => {
                cookie = parseCookie(item);
                if (cookie && typeof cookie === 'object') {
                  ctx.cookies.set(cookie.name, cookie.value, {
                    maxAge: 864000000,
                    httpOnly: false
                  });
                }
              });
            }
          } else {
            ctx.set(i, context.resHeader[i]);
          }
        }
      }

      ctx.status = context.httpCode;
      ctx.body = context.mockJson;
      return;  
    } catch (e) {
      yapi.commons.log(e, 'error');
      return (ctx.body = {
        errcode: 400,
        errmsg: '解析出错，请检查。Error: ' + e.message,
        data: null
      });
    }
  } catch (e) {
    yapi.commons.log(e, 'error');
    return (ctx.body = yapi.commons.resReturn(null, 409, e.message));
  }
};
