const mongoose = require('mongoose');
const { Schema } = mongoose;

const gameSchema = new Schema({
  leagueId: { type: Number, required: true },
  eventId: { type: Number, required: true },
  leagueName: { type: String, required: true },
  teamHomeName: { type: String, required: true },
  teamAwayName: { type: String, required: true },
  timestamp: { type: Number, required: true },
  matchNumStr: { type: String, required: false }
}, { _id: false });

const relSchema = new Schema({
  jc: { type: gameSchema },
  ps: { type: gameSchema },
  ob: { type: gameSchema },
}, { _id: false });

const relationSchema = new Schema({
  id: { type: Number, required: true },
  rel: { type: relSchema, required: true },
}, {
  toJSON: {
    transform(doc, ret) {
      delete ret._id;
      delete ret.__v;
    }
  },
  toObject: {
    transform(doc, ret) {
      delete ret._id;
      delete ret.__v;
    }
  }
});

const Relation = mongoose.model('Relation', relationSchema);

const childOptions = process.env.NODE_ENV == 'development' ? {
  execArgv: ['--inspect=9228']
} : {};
// const { fork } = require('child_process');
// const events_child = fork('./triangle/eventsMatch.js', [], childOptions);

const axios = require('axios');
const calcTotalProfit = require('../triangle/totalProfitCalc');

const Logs = require('../libs/logs');

const Setting = require('./Setting');

const Request = {
  callbacks: {},
  count: 0,
}

const GAMES = {
  Leagues: {},
  List: {},
  Relations: {},
  Solutions: {},
};

/**
 * 精确浮点数字
 * @param {number} number
 * @param {number} x
 * @returns {number}
 */
const fixFloat = (number, x=2) => {
  return parseFloat(number.toFixed(x));
}

/**
 * 更新联赛列表
 */
const syncLeaguesList = ({ mk, leagues }) => {
  axios.post('https://api.isthe.me/api/p/syncLeague', { mk, leagues })
  .then(res => {
    Logs.out('syncLeaguesList', res.data);
  })
  .catch(err => {
    Logs.out('syncLeaguesList', err.message);
  });
}
const updateLeaguesList = ({ mk, leagues }) => {
  const leaguesList = GAMES.Leagues;
  if (JSON.stringify(leaguesList[mk]) != JSON.stringify(leagues)) {
    leaguesList[mk] = leagues;
    syncLeaguesList({ mk, leagues });
    return leagues.length;
  }
  return 0;
}

/**
 * 获取筛选过的联赛
 */
const getFilteredLeagues = async (mk) => {
  return axios.get(`https://api.isthe.me/api/p/getLeagueTast?mk=${mk}`)
  .then(res => {
    if (res.data.code == 0) {
      return res.data.data;
    }
    return Promise.reject(new Error(res.data.message));
  });
}

/**
 * 更新比赛列表
 */
const syncGamesList = ({ platform, mk, games }) => {
  axios.post('https://api.isthe.me/api/p/syncGames', { platform, mk, games })
  .then(res => {
    Logs.out('syncGamesList', res.data);
  })
  .catch(err => {
    Logs.out('syncGamesList', err.message);
  });
}
const updateGamesList = (({ platform, mk, games } = {}) => {
  syncGamesList({ platform, mk, games });
  return new Promise((resolve, reject) => {
    if (!platform || !games) {
      return reject(new Error('PLATFORM_GAMES_INVALID'));
    }

    const marketType = mk == 0 ? 'early' : 'today';

    let gamesList = games;

    if (platform == 'jc') {
      gamesList = [];

      const gamesEvents = [];
      const gamesOutrights = [];

      games.forEach(game => {
        const { eventId, events, evtime, special, sptime, ...gameInfo } = game;
        gamesList.push({ eventId, ...gameInfo });
        gamesEvents.push({ eventId, events, evtime });
        gamesOutrights.push({ parentId: eventId, special, sptime });
      });

      updateGamesEvents({ platform, games: gamesEvents, outrights: gamesOutrights });
    }


    const timestamp = Date.now();

    const GAMES_LIST = GAMES.List;

    if (!GAMES_LIST[platform]) {
      GAMES_LIST[platform] = {};
    }
    if (!GAMES_LIST[platform][marketType]) {
      GAMES_LIST[platform][marketType] = { games: gamesList, timestamp };
      return resolve({ add: gamesList.length, del: 0 });
    }

    const oldGames = GAMES_LIST[platform][marketType].games;
    const newGames = gamesList;

    const updateCount = {
      add: 0,
      del: 0,
    };

    const newMap = new Map(newGames.map(item => [item.eventId, item]));
    for (let i = oldGames.length - 1; i >= 0; i--) {
      if (!newMap.has(oldGames[i].eventId)) {
        oldGames.splice(i, 1);
        updateCount.del += 1;
      }
    }

    const oldIds = new Set(oldGames.map(item => item.eventId));
    const relatedGames = Object.values(GAMES.Relations).map(rel => rel[platform] ?? {});
    const relatedMap = new Map(relatedGames.map(item => [item.eventId, item]));
    newGames.forEach(item => {
      if (!oldIds.has(item.eventId)) {
        oldGames.push(item);
        updateCount.add += 1;
      }
      if (relatedMap.has(item.eventId)) {
        const relatedGame = relatedMap.get(item.eventId);
        relatedGame.mk = mk;
      }
    });

    GAMES_LIST[platform][marketType].timestamp = timestamp;

    resolve(updateCount);
  });
});

/**
 * 更新比赛盘口
 */
const updateGamesEvents = ({ platform, mk, games, outrights }) => {
  return new Promise((resolve, reject) => {
    if (!platform || (!games && !outrights)) {
      return reject(new Error('PLATFORM_GAMES_INVALID'));
    }

    const relatedGames = Object.values(GAMES.Relations).map(rel => rel[platform] ?? {});

    if (!relatedGames.length) {
      return resolve({ update: 0 });
    }

    const updateCount = {
      update: 0
    };

    const relatedMap = new Map(relatedGames.map(item => [item.eventId, item]));

    games?.forEach(game => {
      const { eventId, evtime, events } = game;
      const relatedGame = relatedMap.get(eventId);
      if (relatedGame) {
        relatedGame.evtime = evtime;
        relatedGame.events = events;
        updateCount.update ++;
      }
    });

    outrights?.forEach(outright => {
      const { parentId, sptime, special } = outright;
      const relatedGame = relatedMap.get(parentId);
      if (relatedGame) {
        relatedGame.sptime = sptime;
        relatedGame.special = special;
        updateCount.update ++;
      }
    });

    resolve(updateCount);
  });
}

/**
 * 获取比赛列表
 */
const getGamesList = () => {
  const gamesListMap = {};
  Object.keys(GAMES.List).forEach(platform => {
    const { today, early } = GAMES.List[platform];
    const todayList = today?.games ?? [];
    const earlyList = early?.games ?? [];
    const timestamp_today = today?.timestamp ?? 0;
    const timestamp_early = early?.timestamp ?? 0;
    const timestamp = Math.max(timestamp_today, timestamp_early);
    gamesListMap[platform] = {
      games: [...todayList, ...earlyList],
      timestamp,
      timestamp_today,
      timestamp_early,
    }
  });
  return gamesListMap;
}

/**
 * 获取比赛盘口
 */
const getGamesEvents = ({ platform, relIds = [] } = {}) => {
  const idSet = new Set(relIds);
  const relations = { ...GAMES.Relations };
  Object.keys(relations).forEach(id => {
    if (idSet.size && !idSet.has(id)) {
      delete relations[id];
    }
  });
  if (platform) {
    return Object.values(relations).map(rel => rel[platform] ?? {});
  }
  const gamesEvents = {};
  Object.values(relations).forEach(rel => {
    Object.keys(rel).forEach(platform => {
      const game = rel[platform] ?? {};
      const { eventId, events, special } = game;
      if (!gamesEvents[platform]) {
        gamesEvents[platform] = {};
      }
      gamesEvents[platform][eventId] = { ...events, ...special };
    });
  });
  return gamesEvents;
}

/**
 * 更新关联比赛
 */
const updateGamesRelation = async (relation) => {
  const { id, rel } = relation;
  return Relation.findOne({ id })
  .then(result => {
    if (!result) {
      const gameRelation = new Relation(relation);
      return gameRelation.save();
    }
    return Relation.updateOne({ id }, { $set: { rel } });
  })
  .then(result => {
    GAMES.Relations[id] = rel;
    return result;
  });
}

/**
 * 删除关联比赛
 */
const removeGamesRelation = async (id) => {
  if (!id) {
    return Promise.reject(new Error('ID_INVALID'));
  }
  return Relation.deleteOne({ id })
  .then(result => {
    delete GAMES.Relations[id];
    return result;
  });
}

/**
 * 获取关联比赛
 */
const getGamesRelation = (listEvents) => {
  const relationIds = Object.keys(GAMES.Relations);
  if (listEvents) {
    return relationIds.map(id => {
      const rel = GAMES.Relations[id];
      return { id, rel };
    });
  }
  return relationIds.map(id => {
    const rel = { ...GAMES.Relations[id] };
    Object.keys(rel).forEach(platform => {
      const game = { ...rel[platform] };
      delete game.events;
      delete game.evtime;
      delete game.special;
      delete game.sptime;
      rel[platform] = game;
    });
    return { id, rel };
  });
}

/**
 * 清理关联比赛
 */
const relationsCleanup = () => {
  const expireTime = Date.now() - 1000*60*5;
  const gamesRelation = getGamesRelation();
  gamesRelation.forEach(item => {
    const { id, rel } = item;
    const expire = Object.values(rel).find(event => {
      return event.timestamp <= expireTime;
    });
    if (expire) {
      Logs.out('relation cleanup', id);
      removeGamesRelation(id);
    }
    return true;
  });
}

/**
 * 从数据库中同步关联比赛
 */
const relationSync = () => {
  Relation.find().then(relation => relation.forEach(item => {
    const { id, rel } = item.toObject();
    GAMES.Relations[id] = rel;
  }));
}

/**
 * 更新中单方案
 */
const updateSolutions = (solutions) => {
  if (solutions?.length) {
    const solutionsHistory = GAMES.Solutions;
    const updateIds = { add: [], update: [] }
    solutions.forEach(item => {
      const { sid, sol: { win_average } } = item;

      if (!solutionsHistory[sid]) {
        solutionsHistory[sid] = item;
        updateIds.add.push({ sid, win_average });
        return;
      }

      const historyWinAverage = solutionsHistory[sid].sol.win_average;
      if (win_average != historyWinAverage) {
        solutionsHistory[sid] = item;
        updateIds.update.push({ sid, win_average, his_average: historyWinAverage, diff: fixFloat(win_average - historyWinAverage) });
        return;
      }

      const { timestamp } = item;
      solutionsHistory[sid].timestamp = timestamp;

    });
    // if (updateIds.add.length || updateIds.update.length) {
    //   const solutionsList = Object.values(solutionsHistory).sort((a, b) => b.sol.win_average - a.sol.win_average);
    //   Logs.outDev('solutions history update', JSON.stringify(solutionsList, null, 2), JSON.stringify(updateIds, null, 2));
    // }
  }
}

/**
 * 获取中单方案
 */
const getSolutions = async () => {
  const solutionsList = Object.values(GAMES.Solutions);
  const relIds = solutionsList.map(item => item.info.id);
  const gamesEvents = getGamesEvents({ relIds });
  const gamesRelations = getGamesRelation();
  const relationsMap = new Map(gamesRelations.map(item => [item.id, item.rel]));
  const solutions = solutionsList.sort((a, b) => b.sol.win_average - a.sol.win_average).map(item => {
    const { info: { id } } = item;
    const relation = relationsMap.get(id);
    return {
      ...item,
      info: { id, ...relation }
    }
  });
  return { solutions, gamesEvents };
}

/**
 * 清理中单方案
 */
const solutionsCleanup = () => {
  const solutionsHistory = GAMES.Solutions;
  Object.keys(solutionsHistory).forEach(sid => {
    const { timestamp } = solutionsHistory[sid];
    const nowTime = Date.now();
    if (nowTime - timestamp > 1000*60) {
      delete solutionsHistory[sid];
      Logs.out('solution history timeout', sid);
      return;
    }
    const solution = solutionsHistory[sid];
    const eventTime = solution.info.timestamp;
    if (nowTime > eventTime) {
      delete solutionsHistory[sid];
      Logs.out('solution history expired', sid);
    }
  });
}

const getTotalProfit = (sid1, sid2, gold_side_inner) => {
  const preSolution = GAMES.Solutions[sid1];
  const subSolution = GAMES.Solutions[sid2];
  const relId1 = preSolution?.info?.id;
  const relId2 = subSolution?.info?.id;
  const relIds = [relId1, relId2];
  const gamesEvents = getGamesEvents({ relIds });

  const gamesRelations = getGamesRelation();
  const relationsMap = new Map(gamesRelations.map(item => [item.id, item.rel]));
  const preRelation = relationsMap.get(relId1);
  const subRelation = relationsMap.get(relId2);
  preSolution.info = { id: relId1, ...preRelation };
  subSolution.info = { id: relId2, ...subRelation };

  const sol1 = preSolution?.sol;
  const sol2 = subSolution?.sol;
  if (!sol1 || !sol2 || !gold_side_inner) {
    return {};
  }
  const profit = calcTotalProfit(sol1, sol2, gold_side_inner);
  return { profit, preSolution, subSolution, gamesEvents };
}

// const getSetting = async () => {
//   return Setting.get();
// }

// const getDataFromChild = (type, callback) => {
//   const id = ++Request.count;
//   Request.callbacks[id] = callback;
//   events_child.send({ method: 'get', id, type });
// }

// events_child.on('message', async (message) => {
//   const { callbacks } = Request;
//   const { method, id, type, data } = message;
//   if (method == 'get' && id) {
//     let responseData = null;
//     if (type == 'getGamesRelation') {
//       responseData = getGamesRelation(true);
//     }
//     else if (type == 'getSetting') {
//       responseData = await getSetting();
//     }
//     // else if (type == 'getSolutionHistory') {
//     //   responseData = getSolutionHistory();
//     // }
//     events_child.send({ type: 'response', id, data: responseData });
//   }
//   else if (method == 'post') {
//     if (type == 'updateSolutions') {
//       updateSolutions(data);
//     }
//   }
//   else if (method == 'response' && id && callbacks[id]) {
//     callbacks[id](data);
//     delete callbacks[id];
//   }
// });

relationSync();
setInterval(() => {
  relationsCleanup();
}, 5000);

setInterval(() => {
  solutionsCleanup();
}, 1000*30);

module.exports = {
  updateLeaguesList, getFilteredLeagues,
  updateGamesList, updateGamesEvents, getGamesList,
  updateGamesRelation, getGamesRelation, removeGamesRelation,
  getGamesEvents, getSolutions, getTotalProfit,
}