/**
 * 问卷设计中涉及到的一些 【逻辑】 中公共方法
 */
import config from '@/config';
import { showDialog } from 'vant';
const advancedQuesTypeList = [
  {
    name: 'Maxdiff',
    icon: '&#xe70c;',
    check: false,
    type: 105
  },
  {
    name: 'CBC',
    icon: '&#xe70d;',
    check: false,
    type: 103
  },
  {
    name: 'BPTO',
    icon: '&#xe70d;',
    check: false,
    type: 104
  },
  {
    name: 'PSM',
    icon: '&#xe708;',
    check: false,
    type: 101
  },
  {
    name: 'KANO',
    icon: '&#xe710;',
    check: false,
    type: 102
  }
];
// 高级题型的 question_type
const advancedTypes = advancedQuesTypeList.map((i) => i.type);

const performance = {
  start(/* label */) {
    if (config.currentMode === 'dev') {
      // console.time(`${label} Performance`);
    }
  },
  end(/* label */) {
    if (config.currentMode === 'dev') {
      // console.timeEnd(`${label} Performance`);
    }
  },
  log(/* ...rest */) {
    if (config.currentMode === 'dev') {
      // console.log(...rest);
    }
  }
};

function isNullish(value) {
  return value === undefined || value === null;
}

/**
 * 修改(添加、移动、删除)问卷 题目、分页，编辑（添加、删除）逻辑 时，判断循环逻辑是否满足
 * 的异步方法，感觉题多了这个方法执行会很慢。。。
 * @param rest
 * @return {Promise<boolean>}
 */
export async function loopingAvailableAsync(...rest) {
  return Promise.resolve(loopingAvailable(...rest));
}

/**
 * 修改(添加、移动、删除)问卷 题目、分页，编辑（添加、删除）逻辑 时，判断循环逻辑是否满足
 * @param cycles 循环逻辑列表
 * @param questions 被操作后的问题列表
 * @param logics 逻辑列表
 * @param isPerPage 是否是每页一道题
 * @param reason 提示信息
 * @return {boolean} 是否满足循环逻辑
 */
export function loopingAvailable({ cycles, questions, logics, isPerPage, reason } = {}) {
  const performanceName = 'CheckLoopingAvailable';
  performance.start(performanceName);

  let result = true;
  let prompt = reason || '';

  if (!cycles.length) {
    // 没有配置循环逻辑
    performance.end(performanceName);
    return result;
  }

  const quizList = generateQuestionPages({ questions, logics, isPerPage });
  const cycleList = generateCyclePages(cycles, quizList);
  const logicList = generateLogicPages(logics, quizList);

  // 最后一页的页码
  const lastPage = quizList.reduce((prev, curr) => Math.max(prev, curr.$page || 0), 0);

  // console.log('questions', questions, quizList);
  // console.log('cycles', cycles, cycleList);
  // console.log('logics', logics, logicList);
  // console.log('last page', lastPage);

  for (let i = 0; i < cycleList.length; i += 1) {
    const { index, page, startPage, endPage } = cycleList[i] || {};

    // 循环关联问题被删除时，才会出现这种情况
    if (!quizList.find((j) => j.question_index === index)) {
      result = false;
      prompt = '问题被循环关联，不能删除';
      break;
    }

    if ((startPage && startPage <= page) || (endPage && endPage <= page)) {
      result = false;
      prompt = '问题被循环关联，不能位于循环题组后';
      break;
    }

    // 循环关联问题位于自己或其它循环的循环题组分页中间
    if (page && cycleList.some((j) => j.startPage < page && page < j.endPage)) {
      result = false;
      prompt = '问题被循环关联，不能位于循环题组中';
      break;
    }

    // 循环的循环题组，修改问题或分页的时候应该不会出现这种情况
    if (startPage && endPage && startPage > endPage) {
      result = false;
      prompt = '循环题组的起始分页应该小于结束分页';
      break;
    }

    // 删除分页，或者删除自带分页的题，导致分页变化，校验分页是否包含了所有的循环题组分页
    if (endPage > lastPage) {
      result = false;
      prompt = '请勿在循环题组中编辑分页，请调整循环题组后再试';
      break;
    }

    // 循环题组分页和跳转逻辑有冲突
    if (
      logicList.some((j) => {
        // 跳转到这几个的时候，循环不校验，只校验带题的
        // 正常完成、提前终止、配额超限
        if ([-1, -2, -3].includes(Math.min(...j))) {
          return false;
        }

        return isCross(j, [startPage, endPage]);
      })
    ) {
      result = false;
      prompt = '循环题组分页区间与跳转逻辑冲突，请修改后再试';
      break;
    }
  }

  if (!result && (reason || prompt)) {
    showDialog({
      title: '无法操作',
      width: '450px',
      message: reason || prompt
    });
  }

  performance.end(performanceName);
  return result;
}

/**
 * 给问题列表中的问题添加，问题所在的页码字段（$page）
 * 说明：
 * 1. 需要过滤掉没有题的空白页
 * 2. 高级题型的题前后需要加分页
 * 3. 添加了逻辑的基础题型前后需要加分页
 * @param questions
 * @param logics
 * @param isPerPage
 * @param addon 自定义额外操作，是一个方法
 * @return {array} 题目列表，包括题和分页
 */
export function generateQuestionPages({ questions, logics, isPerPage, addon }) {
  if (!questions?.length) {
    return questions || [];
  }

  let page = 1;
  // 计数，在 questions 有几个分页的对象
  let pageObjectCount = 0;
  return questions.map((item, index, arr) => {
    // 需要自动加分页的题型：高级题型及知情同意书
    const asBlock = [...advancedTypes, 23];
    // 前一道题是高级题型
    const isPrevAdv = asBlock.includes(arr[index - 1]?.question_type);
    // 当前题是高级题型
    const isAdv = asBlock.includes(item?.question_type);
    // const isPrevAdv = advancedTypes.includes(arr[index - 1]?.question_type); // 前一道题是高级题型
    // const isAdv = advancedTypes.includes(item?.question_type); // 当前题是高级题型

    // 前一道题配置了跳转逻辑
    const isPrevLogic = !!logics.find(
      (logic) => logic.question_index === arr[index - 1]?.question_index
    );
    // 当前题配置了跳转逻辑
    const isLogic = !!logics.find((logic) => logic.question_index === item.question_index);
    // 需要在该题前后添加分页
    const isSurroundedByPage = isAdv || isLogic;
    // 前一道题是一个分页对象
    const isPrevPage = !!arr[index - 1]?.page;
    // 前一道题是一道题而不是一个分页对象
    const prevIsQuestion = !arr[index - 1]?.page;

    if (!index) {
      page = 1;
      pageObjectCount = 0;
    }

    if (item.page) {
      pageObjectCount += 1;
      if (index && prevIsQuestion && !isPrevLogic && !isPrevAdv) {
        // 过滤掉没有题的空白页
        page += 1;
      }
    } else {
      if (index && !isPrevPage && !isPrevAdv && !isPrevLogic && isSurroundedByPage) {
        page += 1;
      }
      item.$page = isPerPage ? index + 1 - pageObjectCount : page;
      if (isSurroundedByPage) {
        page += 1;
      }
    }

    if (addon) {
      addon(item);
    }

    return item;
  });
}

/**
 * 格式化循环列表，便于后续使用
 * @param cycles
 * @param questions
 * @return {array} 格式化后的循环列表
 */
export function generateCyclePages(cycles, questions) {
  if (!cycles?.length) {
    return cycles || [];
  }

  return cycles.map((cycle) => ({
    index: cycle.question_index,
    page: getPageByQuestionIndex(cycle.question_index, questions),
    startPage: cycle.first_page,
    endPage: cycle.last_page
  }));
}

/**
 * 逻辑关联的页码，便于后续使用
 * @param logics {*[]}
 * @param questions {*[]}
 * @param fillRangeItem {boolean} 用数字填充满 range[index] 数组
 * @return {number[][]} 逻辑关联的页码
 */
export function generateLogicPages(logics, questions, fillRangeItem) {
  if (!logics?.length) {
    return logics || [];
  }

  const pages = [];

  logics.forEach((logic) => {
    if (![0, 2].includes(logic.skip_type)) {
      return;
    }

    let page = [
      getPageByQuestionIndex(logic.logic?.[0]?.question_index, questions),
      getPageByQuestionIndex(logic.question_index, questions),
      getPageByQuestionIndex(logic.skip_question_index, questions) || logic.skip_question_index
    ];
    page = page.filter((i) => !!i);

    if (fillRangeItem) {
      const min = Math.min(...page.filter((i) => i > 0));
      const max = Math.max(...page);
      page = generateRange(min, max);
    }

    pages.push(page);
  });

  return pages;
}

/**
 * 通过问题的 question_index 找到问题在第几页
 * @param questionIndex
 * @param questions
 * @return {number|*|undefined}
 */
export function getPageByQuestionIndex(questionIndex, questions) {
  return questions.find((i) => i.question_index === questionIndex)?.$page || undefined;
}

/**
 * 生成一个从 start 开始到 end 结束的数组
 * @param start {number}
 * @param end {number}
 * @return {number[]}
 */
export function generateRange(start, end) {
  if (isNullish(start) || isNullish(end)) {
    return [];
  }

  if (isNaN(Number(start)) || isNaN(Number(end))) {
    return [];
  }

  let i = start;
  const range = [];
  while (i <= end) {
    range.push(i);
    i += 1;
  }

  return range;
}

/**
 * 判断逻辑与循环分组，是否不合理
 * @param range1 {array} 跳转逻辑的分页数组
 * @param range2 {array} 循环分组的分页数组
 * @return {boolean} true 不合理， false 合理
 */
export function isCross(range1, range2) {
  if (!range1 || !range2) {
    return false;
  }

  const parsedRange1 = range1.slice(1);
  const judge = range1[0];

  const start1 = Math.min(...parsedRange1);
  const end1 = Math.max(...parsedRange1);
  const start2 = range2[0];
  const end2 = range2[1];

  // 跳转逻辑的方向，true 为从前向后跳转
  const isPlainSequence = parsedRange1[0] === start1;

  if (isNullish(start1) || isNullish(end1) || end1 < 0 || (isNullish(start2) && isNullish(end2))) {
    return false;
  }

  // [judge, start1, end1];  // isPlainSequence
  // [start1, judge, end1];  // isPlainSequence || !isPlainSequence
  //
  // [start2, end2];

  // 逻辑在循环之前
  const isLeft = isNullish(start2) ? end1 < end2 : end1 < start2;
  // 逻辑在循环之后
  const isRight = isNullish(end2)
    ? start2 < start1 && start2 < judge
    : end2 < start1 && end2 < judge;
  // 不相交也不包含
  const isSibling = isLeft || isRight;

  // 逻辑包含循环
  const contain =
    (isPlainSequence &&
      (((isNullish(start2) || isSequence(judge, start2, start1)) &&
        (isNullish(end2) || isSequence(judge, end2, start1))) ||
        ((isNullish(start2) || isSequence(start1, start2, end1)) &&
          (isNullish(end2) || isSequence(start1, end2, end1))))) ||
    (!isPlainSequence &&
      (judge < start1
        ? ((isNullish(start2) || isSequence(judge, start2, start1)) &&
            (isNullish(end2) || isSequence(judge, end2, start1))) ||
          ((isNullish(start2) || isSequence(start1, start2, end1)) &&
            (isNullish(end2) || isSequence(start1, end2, end1)))
        : ((isNullish(start2) || isSequence(start1, start2, judge)) &&
            (isNullish(end2) || isSequence(start1, end2, judge))) ||
          ((isNullish(start2) || isSequence(judge, start2, end1)) &&
            (isNullish(end2) || isSequence(judge, end2, end1)))));
  // 循环存在封闭区间，并且循环包含逻辑
  const contained =
    !isNullish(start2) &&
    !isNullish(end2) &&
    // [judge, start1, end1];
    ((isPlainSequence && start2 <= judge && end1 <= end2) ||
      // [judge, start1, end1];
      // [start1, judge, end1];
      (!isPlainSequence && start2 <= start1 && start2 <= judge && end1 <= end2));
  // 循环不存在封闭区间
  const unCircled =
    (!isNullish(start2) &&
      isNullish(end2) &&
      ((isPlainSequence && start2 === judge) || (!isPlainSequence && judge < start1)
        ? start2 === judge
        : start2 === start1)) ||
    (isNullish(start2) && !isNullish(end2) && end2 === end1);

  return !(isSibling || contain || contained || unCircled);
}

function isSequence(s1, s2, s3, equal) {
  return equal ? s1 <= s2 && s2 <= s3 : s1 < s2 && s2 < s3;
}

/**
 * 调用保存问题接口前，检查是否有问题受到循环影响需要重新保存，如果有则重新保存一下这道题，没有则不需要特殊处理
 * bugfix for : 有循环的问卷发布后，再次编辑问卷，将循环题组内的问题移除循环题组，
 *              再次发布，后端不处理被移除的问题 title，导致该题的 title 仍保持上次发布时的值
 *              错误格式一般为：B3.1 正确格式一般为：B3
 *              导致作答出现错误，例如：引用找不到题
 * 从 store 里查出修改前和修改后的问题、分页、循环；比较修改前后问题是否被移除了某个循环题组；修改 quesSaveParam.newQuestion；
 * @param quesSaveParam 将要保存的数据，会被此方法修改的字段：quesSaveParam.newQuestion
 * @param store
 */
export function updateNewQuestionsByLoopingEffect(quesSaveParam, store) {
  const { questionInfoBeforeModified = {}, questionInfo = {} } =
    JSON.parse(JSON.stringify(store.state.common)) || {};

  const oldPages = questionInfoBeforeModified.survey.pages;
  const newQuestions = questionInfo.questions;
  const newPages = questionInfo.survey.is_one_page_one_question
    ? questionInfo.questions.filter((i) => i.question_index).map((i) => [i.question_index])
    : questionInfo.survey.pages;
  const cycles = questionInfo.cycle_pages || [];

  if (!cycles.length) {
    return;
  }

  const moveOutOfCycleQuestionIndex = [];
  const cyclePages = cycles.map((i) => [i.first_page, i.last_page]).filter((i) => i[0] && i[1]);
  cyclePages.forEach((i) => {
    const start = i[0] - 1;
    const end = i[1] - 1;

    for (let j = start; j <= end; j += 1) {
      if (oldPages[j]?.join(',') !== newPages[j]?.join(',')) {
        oldPages[j]?.forEach((k) => {
          if (!newPages?.[j]?.includes(k)) {
            moveOutOfCycleQuestionIndex.push(k);
          }
        });
      }
    }
  });
  const movedOutOfCycleQuestions = newQuestions.filter((i) =>
    moveOutOfCycleQuestionIndex.includes(i.question_index)
  );

  if (movedOutOfCycleQuestions.length) {
    if (!quesSaveParam.newQuestion) {
      quesSaveParam.newQuestion = [];
    }
    quesSaveParam.newQuestion.push(...movedOutOfCycleQuestions);
  }
}
