import { Client } from './Client';
import 'mocha';
import { PinusWSClient } from 'pinus-robot-plugin';
import { expect } from 'chai';
import { addItemsIfNotEnough, checkDisplayItems, checkSuccessResponse, checkTimeStamp, checkWarJson } from './CheckPatten';
import { PVP } from '../app/pubUtils/dicParam';
import { DEBUG_MAGIC_WORD } from '../app/consts';
import { getRandSingleEelm } from './pureUtil';

describe('pvp测试', function () {
  let pinusClient: PinusWSClient;
  let roleInfo;
  let oppPlayers = [];
  let setAttackCnt = 0;
  let buyAttackCnt = 0;
  let refOppCnt = 0;
  let challengeCnt = 0;
  let attack;
  let receivedBox = [];
  let hisScore = 0;

  before(function (done) {
    const c = new Client();
    const timer = setInterval(() => {
      if (c.client && c.roleInfo) {
        pinusClient = c.client;
        roleInfo = c.roleInfo;
        clearInterval(timer);
        done();
      }
    }, 500);
  });

  after(function (done) {
    pinusClient.disconnect();
    // disconnect 后等待 500ms，供服务器清理环境、退出频道等
    setTimeout(() => {
      done();
    }, 500);
  });

  it('获得主界面信息', function (done) {
    getData(pinusClient, false, function (data) {
      oppPlayers = data.oppPlayers;
      setAttackCnt = data.setAttackCnt;
      buyAttackCnt = data.buyAttackCnt;
      refOppCnt = data.refOppCnt;
      challengeCnt = data.challengeCnt;
      attack = data.attack;
      done();
    }); // 先不结算赛季
  });

  it('保存防守阵容', function (done) {
    let myHeroes = roleInfo.heros || [];
    let heroes = [];
    for (let i = 0; i < 6; i++) {
      if (myHeroes.length - 1 < i) break;
      heroes.push({
        actorId: myHeroes[i].hid,
        ai: Math.floor(Math.random() * 2),
        dataId: 2001 + i,
        order: i + 1
      });
    }
    pinusClient.request('battle.pvpHandler.saveDefense', { warId: 4501, buff: 50101, heroes }, (res) => {
      checkSuccessResponse(res);
      checkDefense(res.data.defense);
      done();
    });
  });

  function debugResetAttackCnt(cb) {
    pinusClient.request('battle.pvpHandler.debugResetAttackCnt', { magicWord: DEBUG_MAGIC_WORD }, (res) => {
      setAttackCnt = 0;
      buyAttackCnt = 0;
      cb();
    })
  }

  it('购买保存挑战阵容次数', function (done) {
    if (buyAttackCnt >= PVP.PVP_BUY_SET_ATTACK_CNT) {
      debugResetAttackCnt(() => {
        buyAttackCount();
      });
    } else {
      buyAttackCount();
    }

    function buyAttackCount() {
      pinusClient.request('battle.pvpHandler.buyAttackCnt', { count: 1 }, res => {
        checkSuccessResponse(res);
        expect(res.data.refOppCnt).to.be.a('number');
        expect(res.data.setAttackCnt).to.be.a('number');
        expect(res.data.buyAttackCnt).to.be.a('number');
        refOppCnt = res.data.refOppCnt;
        setAttackCnt = res.data.setAttackCnt;
        buyAttackCnt = res.data.buyAttackCnt;
        if (res.data.oppPlayers) {
          checkOppPlayers(res.data.oppPlayers);
          oppPlayers = res.data.oppPlayers;
        }
        done();
      })
    }
  });

  it('保存挑战阵容', function (done) {
    if (setAttackCnt >= buyAttackCnt + PVP.PVP_SET_ATTACK_FREE_CNT) { // 次数不足
      debugResetAttackCnt(() => {
        saveAttack();
      });
    } else {
      saveAttack();
    }
    function saveAttack() {
      let myHeroes = roleInfo.heros || [];
      let heroes = [];
      for (let i = 0; i < 6; i++) {
        if (myHeroes.length - 1 < i) break;
        heroes.push({
          actorId: myHeroes[i].hid,
          order: i + 1
        });
      }
      pinusClient.request('battle.pvpHandler.saveAttack', { heroes }, (res) => {
        checkSuccessResponse(res);
        checkAttack(res.data.attack);
        checkOppPlayers(res.data.oppPlayers);
        expect(res.data.setAttackCnt).to.equal(setAttackCnt + (!!attack ? 1 : 0)); // 第一次保存不占次数
        attack = res.data.attack;
        setAttackCnt = res.data.setAttackCnt;
        oppPlayers = res.data.oppPlayers;
        done();
      });
    }
  });

  function debugResetRefOppCnt(cb) {
    pinusClient.request('battle.pvpHandler.debugResetRefOppCnt', { magicWord: DEBUG_MAGIC_WORD }, (res) => {
      refOppCnt = 0
      cb();
    })
  }
  it('刷新匹配对手', function (done) {
    if (refOppCnt >= 8) { // 次数不足
      debugResetRefOppCnt(() => {
        refreshOppPlayer();
      });
    } else {
      refreshOppPlayer();
    }
    function refreshOppPlayer() {
      addItemsIfNotEnough(roleInfo, pinusClient, 31002, 50, () => {
        pinusClient.request('battle.pvpHandler.refreshOppPlayer', {}, (res) => {
          checkSuccessResponse(res);
          checkOppPlayers(res.data.oppPlayers);
          expect(res.data.refOppCnt).to.equal(refOppCnt + 1);
          expect(res.data.setAttackCnt).to.equal(setAttackCnt);
          setAttackCnt = res.data.setAttackCnt;
          refOppCnt = res.data.refOppCnt;
          oppPlayers = res.data.oppPlayers;
          done();
        });
      });
    }
  });

  let chosenOpp;
  let curWarId;
  let battleCode;
  let chosenOppHeroes;

  it('获取对手阵容具体信息', function (done) {
    chosenOpp = getRandSingleEelm(oppPlayers);
    pinusClient.request('battle.pvpHandler.getOppPlayer', { roleId: chosenOpp.roleId }, (res) => {
      checkSuccessResponse(res);
      expect(res.data.roleId).to.equal(chosenOpp.roleId);
      expect(res.data.pos).to.equal(chosenOpp.pos);
      expect(res.data.warId).to.be.a('number');
      curWarId = res.data.warId
      expect(res.data.buff).to.be.a('number');
      checkWarJson(res.data.heroes);
      chosenOppHeroes = res.data.heroes;
      done();
    });
  });

  it('获取对手详细信息', function(done) {
    pinusClient.request('battle.pvpHandler.getPlayerDetail', { roleId: chosenOpp.roleId }, (res) => {
        checkSuccessResponse(res);
        expect(res.data.roleId).to.be.a('string');
        expect(res.data.roleName).to.be.a('string');
        expect(res.data.lv).to.be.a('number');
        expect(res.data.title).to.be.a('number');
        expect(res.data.head).to.be.a('number');
        expect(res.data.frame).to.be.a('number');
        expect(res.data.spine).to.be.a('number');
        expect(res.data.defCe).to.be.a('number');
        expect(res.data.score).to.be.a('number');
        expect(res.data.rank).to.be.a('number');
        expect(res.data.pLv).to.be.a('number');
        expect(res.data.heroes).to.be.a('array');
        res.data.heroes.forEach(hero => {
            expect(hero.actorId).to.be.a('number');
            expect(hero.isDefense).to.be.a('boolean');
            expect(hero.skinId).to.be.a('number');
            expect(hero.lv).to.be.a('number');
            expect(hero.star).to.be.a('number');
            expect(hero.colorStar).to.be.a('number');
            expect(hero.quality).to.be.a('number');
            if(!!!hero.score) {
              console.warn("hero.score undefined!");
            } else {
              expect(hero.score).to.be.a('number');
            }
        });
        done();
    });
  });

  function debugAddChallengeCnt(cb) {
    pinusClient.request('battle.pvpHandler.debugAddChallengeCnt', { magicWord: DEBUG_MAGIC_WORD, challengeCnt: 10 }, (res) => {
      checkSuccessResponse(res);
      expect(res.data.challengeCnt).to.be.a('number');
      challengeCnt = res.data.challengeCnt;
      cb();
    })
  }

  it('开始挑战', function (done) {
    if(challengeCnt <= 0) {
      debugAddChallengeCnt(() => {
        pvpStart();
      })
    } else {
      pvpStart();
    }

    function pvpStart() {
      chosenOpp = getRandSingleEelm(oppPlayers);
      pinusClient.request('battle.pvpHandler.pvpStart', { warId: curWarId, roleId: chosenOpp.roleId }, (res) => {
        checkSuccessResponse(res);
        expect(res.data.battleCode).to.a('string');
        battleCode = res.data.battleCode;
        done();
      });
    }
  });

  it('结束挑战', function (done) {
    if(!attack || !attack.heroes) {
      console.error('没有挑战阵容'); return;
    }
    function ranDamage()  {
      return Math.floor(Math.random() * 1000)
    }
    let myHeroes = attack.heroes.map(cur => {
      return { hid: cur.actorId, damage: ranDamage(), heal: ranDamage(), underDamage: ranDamage()}
    });
    let oppHeroes = chosenOppHeroes.filter(cur => cur.actorId > 0).map(cur => {
      return { hid: cur.actorId, damage: ranDamage(), heal: ranDamage(), underDamage: ranDamage() }
    })
    pinusClient.request('battle.pvpHandler.pvpEnd', { battleCode, isSuccess: true, myHeroes, oppHeroes }, (res) => {
      checkSuccessResponse(res);
      expect(res.data.score).to.a('number');
      expect(res.data.myRank).to.a('number');
      expect(res.data.winStreakNum).to.a('number');

      expect(res.data.heroScores).to.be.a('array');
      res.data.heroScores.forEach(heroScore => {
        expect(heroScore.hid).to.be.a('number');
        expect(heroScore.addScore).to.be.a('number');
        expect(heroScore.plusScore).to.be.a('number');
        expect(heroScore.score).to.be.a('number');
      });

      checkAttack(res.data.attack);
      checkOppPlayers(res.data.oppPlayers);
      oppPlayers = res.data.oppPlayers;
      expect(res.data.challengeCnt).to.equal(challengeCnt - 1);
      checkTimeStamp(res.data.challengeRefTime, 10, true);

      done();
    });
  });

  it('获得排行榜', function(done) {
    pinusClient.request('battle.pvpHandler.getRank', { }, (res) => {
        checkSuccessResponse(res);
        expect(res.data.ranks).to.be.a('array');
        res.data.ranks.forEach(rank => {
            checkRank(rank);
        });
        checkRank(res.data.myRank);
        done();
    });
  });

  function debugResetBox(cb) {
    pinusClient.request('battle.pvpHandler.debugResetBox', { magicWord: DEBUG_MAGIC_WORD }, (res) => {
      receivedBox = [];
      hisScore = 5000;
      cb();
    })
  }

  it.skip('领取巅峰之路奖励', function(done) {
    let id = Math.floor(Math.random() * 10);
    if(receivedBox.includes(id) || hisScore < 5000) {
      debugResetBox(() => {
        receiveBox();
      })
    } else {
      receiveBox();
    }

    function receiveBox() {
      receiveSingleBox(id, 1, () => {
        receiveSingleBox(id, 2, () => {
          done();
        });
      })
    }

    function receiveSingleBox(id, count, cb) {
      pinusClient.request('battle.pvpHandler.receiveBox', { id }, (res) => {
        if(count == 1) {
          checkSuccessResponse(res);
          checkDisplayItems(res.data.goods);
          expect(res.data.receivedBox).to.be.a('array');
          res.data.receivedBox.forEach(box => {
            expect(box).to.be.a('number');
          });
        } else {
          expect(res.code).to.equal(20804)
        }
        cb()
      });
    }
  });
  
  
  it('获得战报', function(done) {
    pinusClient.request('battle.pvpHandler.getRec', { }, (res) => {
        checkSuccessResponse(res);
        expect(res.data.list).to.be.a('array');
        res.data.list.forEach(rec => {
          expect(rec.roleId1).to.be.a('string');
          expect(rec.roleId2).to.be.a('string');
          expect(rec.warId).to.be.a('number');
          checkRecInfo(rec.attackInfo);
          checkRecInfo(rec.defenseInfo);
        });
        done();
    });
  });
  
});

function checkRank(rank) {
  expect(rank.rank).to.be.a('number');
  expect(rank.roleId).to.be.a('string');
  expect(rank.roleName).to.be.a('string');
  expect(rank.head).to.be.a('number');
  expect(rank.frame).to.be.a('number');
  expect(rank.spine).to.be.a('number');
  expect(rank.num).to.be.a('number');
  expect(rank.title).to.be.a('number');
  expect(rank.lv).to.be.a('number');
  expect(rank.ce).to.be.a('number');
}

function getData(pinusClient, seasonEnd: boolean, cb) {

  pinusClient.request('battle.pvpHandler.getData', {}, (res) => {
    checkSuccessResponse(res);
    expect(res.data.seasonNum).to.be.a('number');
    expect(res.data.seasonEndTime).to.be.a('number');
    expect(res.data.myRank).to.be.a('number');

    checkOppPlayers(res.data.oppPlayers);
    checkDefense(res.data.defense);
    checkAttack(res.data.attack);

    expect(res.data.score).to.be.a('number');
    expect(res.data.winStreakNum).to.be.a('number');

    expect(res.data.refOppCnt).to.be.a('number');
    expect(res.data.setAttackCnt).to.be.a('number');
    expect(res.data.buyAttackCnt).to.be.a('number');
    expect(res.data.challengeCnt).to.be.a('number');
    checkTimeStamp(res.data.challengeRefTime, 10, true);

    expect(res.data.receivedBox).to.be.a('array');
    res.data.receivedBox.forEach(box => {
      expect(box).to.be.a('number');
    });
    expect(res.data.hisScore).to.be.a('number');
    expect(res.data.heroScores).to.be.a('array');
    res.data.heroScores.forEach(heroScore => {
      expect(heroScore.isDefense).to.be.a('boolean');
      expect(heroScore.hid).to.be.a('number');
      expect(heroScore.score).to.be.a('number');
    });
    if (seasonEnd) {
      checkSeasonResult(res.data.seasonResult);
    }
    cb(res.data);
  });
}

function checkOppPlayers(oppPlayers) {
  expect(oppPlayers).to.be.a('array');
  oppPlayers.forEach(player => {
    expect(player.pos).to.be.a('number');
    expect(player.roleId).to.be.a('string');
    expect(player.roleName).to.be.a('string');
    expect(player.head).to.be.a('number');
    expect(player.frame).to.be.a('number');
    expect(player.spine).to.be.a('number');
    expect(player.lv).to.be.a('number');
    expect(player.pLv).to.be.a('number');
    expect(player.defCe).to.be.a('number');
    expect(player.addScore).to.be.a('number');
    expect(player.plusScore).to.be.a('number');
    expect(player.rankLv).to.be.a('number');
  });
}

function checkDefense(defense) {
  if (!!defense) {
    expect(defense).to.be.an('object');
    expect(defense.warId).to.be.a('number');
    expect(defense.buff).to.be.a('number');
    expect(defense.heroes).to.be.an('array');
    defense.heroes.forEach(hero => {
      expect(hero.actorId).to.be.a('number');
      expect(hero.ai).to.be.a('number');
      expect(hero.dataId).to.be.a('number');
      expect(hero.order).to.be.a('number');
    });
    expect(defense.score).to.be.a('number');
    expect(defense.pLv).to.be.a('number');
    expect(defense.ce).to.be.a('number');
    if (defense.heroes.length > 0) {
      expect(defense.ce).not.equal(0);
    }
  }
}

function checkAttack(attack) {
  if (!!attack) {
    expect(attack).to.be.an('object');
    expect(attack.heroes).to.be.an('array');
    attack.heroes.forEach(hero => {
      expect(hero.actorId).to.be.a('number');
      if (!!hero.order) {
        expect(hero.order).to.be.a('number');
      }
    });
    expect(attack.score).to.be.a('number');

  }
}

function checkSeasonResult(resultRecord) {
  expect(resultRecord).to.be.a('object');
  checkOldSeasonData(resultRecord.oldSeasonData);
  expect(resultRecord.heroGoods).to.be.an('array');
  resultRecord.heroGoods.forEach(obj => {
    expect(obj.hid).to.be.a('number');
    expect(obj.id).to.be.a('number');
    expect(obj.count).to.be.a('number');
  })
  expect(resultRecord.rankGoods).to.be.an('array');
  resultRecord.rankGoods.forEach(obj => {
    expect(obj.id).to.be.a('number');
    expect(obj.count).to.be.a('number');
  })
}

function checkOldSeasonData(oldSeasonData) {
  expect(oldSeasonData).to.be.an('object');
  expect(oldSeasonData.score).to.be.a('number');
  expect(oldSeasonData.heroScores).to.be.an('array');
  oldSeasonData.heroScores.forEach(heroScore => {
    expect(heroScore.hid).to.be.a('number');
    expect(heroScore.score).to.be.a('number');
  });
  expect(oldSeasonData.seasonNum).to.be.a('number');
  checkTimeStamp(oldSeasonData.seasonEndTime, 10, true);
  expect(oldSeasonData.rankLv).to.be.a('number');
}


function checkRecInfo(info) {
  expect(info.roleId).to.be.a('string');
  expect(info.roleName).to.be.a('string');
  expect(info.lv).to.be.a('number');
  expect(info.spine).to.be.a('number');
  expect(info.head).to.be.a('number');
  expect(info.frame).to.be.a('number');
  expect(info.title).to.be.a('number');
  expect(info.ce).to.be.a('number');
  expect(info.isSuccess).to.be.a('boolean');
  expect(info.score).to.be.a('number');
  checkRecHero(info.heroes);
}

function checkRecHero(heroes) {
  expect(heroes).to.be.an('array');
  heroes.forEach(hero => {
    expect(hero.hid).to.be.a('number');
    expect(hero.skinId).to.be.a('number');
    expect(hero.quality).to.be.a('number');
    expect(hero.star).to.be.a('number');
    expect(hero.colorStar).to.be.a('number');
    expect(hero.lv).to.be.a('number');
    expect(hero.damage).to.be.a('number');
    expect(hero.heal).to.be.a('number');
    expect(hero.underDamage).to.be.a('number');
  })
}