import { isEqual } from 'lodash-es';

export interface DiffType {
  type: string;
  min: number;
}

function getMinDiff(del: number, add: number, modify: number): DiffType {
  let type = null;
  let min = modify;
  if (add < min) {
    min = add;
    type = 'add';
  }
  if (del < min) {
    min = del;
    type = 'del';
  }
  return {
    type,
    min,
  };
}

/*
 * https://en.wikipedia.org/wiki/Levenshtein_distance
 * 计算两条path的编辑距离
 */
const levenshteinDistance = function (source: string, target: string): DiffType[][] {
  const sourceLen = source.length;
  const targetLen = target.length;
  let sourceSegment, targetSegment;
  let temp = 0;
  if (sourceLen === 0 || targetLen === 0) {
    return null;
  }
  const dist = [];
  for (let i = 0; i <= sourceLen; i++) {
    dist[i] = [];
    dist[i][0] = { min: i };
  }
  for (let j = 0; j <= targetLen; j++) {
    dist[0][j] = { min: j };
  }

  for (let i = 1; i <= sourceLen; i++) {
    sourceSegment = source[i - 1];
    for (let j = 1; j <= targetLen; j++) {
      targetSegment = target[j - 1];
      if (isEqual(sourceSegment, targetSegment)) {
        temp = 0;
      } else {
        temp = 1;
      }
      const del = dist[i - 1][j].min + 1;
      const add = dist[i][j - 1].min + 1;
      const modify = dist[i - 1][j - 1].min + temp;
      dist[i][j] = getMinDiff(del, add, modify);
    }
  }
  return dist;
};

export default function fillPathByDiff(source: string, target: string) {
  const diffMatrix = levenshteinDistance(source, target);
  let sourceLen = source.length;
  const targetLen = target.length;
  const changes = [];
  let index = 1;
  let minPos = 1;
  // 如果source和target不是完全不相等
  // @ts-ignore
  if (diffMatrix[sourceLen][targetLen] !== sourceLen) {
    // 获取从source到target所需改动
    for (let i = 1; i <= sourceLen; i++) {
      let min = diffMatrix[i][i].min;
      minPos = i;
      for (let j = index; j <= targetLen; j++) {
        if (diffMatrix[i][j].min < min) {
          min = diffMatrix[i][j].min;
          minPos = j;
        }
      }
      index = minPos;
      if (diffMatrix[i][index].type) {
        changes.push({ index: i - 1, type: diffMatrix[i][index].type });
      }
    }
    // 对source进行增删path
    for (let i = changes.length - 1; i >= 0; i--) {
      index = changes[i].index;
      if (changes[i].type === 'add') {
        // @ts-ignore
        source.splice(index, 0, [].concat(source[index]));
      } else {
        // @ts-ignore
        source.splice(index, 1);
      }
    }
  }

  // source尾部补齐
  sourceLen = source.length;
  if (sourceLen < targetLen) {
    for (let i = 0; i < targetLen - sourceLen; i++) {
      if (source[sourceLen - 1][0] === 'z' || source[sourceLen - 1][0] === 'Z') {
        // @ts-ignore
        source.splice(sourceLen - 2, 0, source[sourceLen - 2]);
      } else {
        // @ts-ignore
        source.push(source[sourceLen - 1]);
      }
    }
  }
  return source;
}
