import { v4 as generateUUID } from 'uuid';
import {
  generateUser,
  generateChallenge,
  createAndPopulateGroup,
  sleep,
  checkExistence,
  translate as t,
} from '../../../../helpers/api-integration/v3';

describe('POST /challenges/:challengeId/winner/:winnerId', () => {
  it('returns error when challengeId is not a valid UUID', async () => {
    const user = await generateUser();

    await expect(user.post(`/challenges/test/selectWinner/${user._id}`)).to.eventually.be.rejected.and.eql({
      code: 400,
      error: 'BadRequest',
      message: t('invalidReqParams'),
    });
  });

  it('returns error when winnerId is not a valid UUID', async () => {
    const user = await generateUser();

    await expect(user.post(`/challenges/${generateUUID()}/selectWinner/test`)).to.eventually.be.rejected.and.eql({
      code: 400,
      error: 'BadRequest',
      message: t('invalidReqParams'),
    });
  });

  it('returns error when challengeId is not for a valid challenge', async () => {
    const user = await generateUser();

    await expect(user.post(`/challenges/${generateUUID()}/selectWinner/${user._id}`)).to.eventually.be.rejected.and.eql({
      code: 404,
      error: 'NotFound',
      message: t('challengeNotFound'),
    });
  });

  context('Selecting winner for a valid challenge', () => {
    let groupLeader;
    let group;
    let challenge;
    let winningUser;
    const taskText = 'A challenge task text';

    beforeEach(async () => {
      const populatedGroup = await createAndPopulateGroup({
        members: 1,
      });

      groupLeader = populatedGroup.groupLeader;
      group = populatedGroup.group;
      winningUser = populatedGroup.members[0]; // eslint-disable-line prefer-destructuring

      challenge = await generateChallenge(groupLeader, group, {
        prize: 1,
      });
      await groupLeader.post(`/challenges/${challenge._id}/join`);

      await groupLeader.post(`/tasks/challenge/${challenge._id}`, [
        { type: 'habit', text: taskText },
      ]);

      await winningUser.post(`/challenges/${challenge._id}/join`);

      await challenge.sync();
    });

    it('returns an error when user doesn\'t have permissions to select winner', async () => {
      await expect(winningUser.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`)).to.eventually.be.rejected.and.eql({
        code: 401,
        error: 'NotAuthorized',
        message: t('onlyLeaderDeleteChal'),
      });
    });

    it('returns an error when winning user isn\'t part of the challenge', async () => {
      const notInChallengeUser = await generateUser();

      await expect(groupLeader.post(`/challenges/${challenge._id}/selectWinner/${notInChallengeUser._id}`)).to.eventually.be.rejected.and.eql({
        code: 404,
        error: 'NotFound',
        message: t('winnerNotFound', { userId: notInChallengeUser._id }),
      });
    });

    it('deletes challenge after winner is selected', async () => {
      await groupLeader.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`);

      await sleep(0.5);

      await expect(checkExistence('challenges', challenge._id)).to.eventually.equal(false);
    });

    it('adds challenge to winner\'s achievements', async () => {
      await groupLeader.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`);

      await sleep(0.5);

      await expect(winningUser.sync()).to.eventually.have.nested.property('achievements.challenges').to.include(challenge.name);
      // 2 because winningUser just joined the challenge, which now awards an achievement
      expect(winningUser.notifications.length).to.equal(2);

      const notif = winningUser.notifications[1];
      expect(notif.type).to.equal('WON_CHALLENGE');
      expect(notif.data).to.eql({
        id: challenge._id,
        name: challenge.name,
        prize: challenge.prize,
        leader: challenge.leader,
      });
    });

    it('gives winner gems as reward', async () => {
      const oldBalance = winningUser.balance;

      await groupLeader.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`);

      await sleep(0.5);

      await expect(winningUser.sync()).to.eventually.have.property('balance', oldBalance + challenge.prize / 4);
    });

    it('doesn\'t gives winner gems if group policy prevents it', async () => {
      const oldBalance = winningUser.balance;
      const oldLeaderBalance = (await groupLeader.sync()).balance;

      await winningUser.updateOne({
        'purchased.plan.customerId': 'group-plan',
      });
      await group.updateOne({
        'leaderOnly.getGems': true,
        'purchased.plan.customerId': 123,
      });

      await groupLeader.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`);

      await sleep(0.5);

      await expect(winningUser.sync()).to.eventually.have.property('balance', oldBalance);
      await expect(groupLeader.sync()).to.eventually.have.property('balance', oldLeaderBalance + challenge.prize / 4);
    });

    it('doesn\'t refund gems to group leader', async () => {
      const oldBalance = (await groupLeader.sync()).balance;

      await groupLeader.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`);

      await sleep(0.5);

      await expect(groupLeader.sync()).to.eventually.have.property('balance', oldBalance);
    });

    it('sets broken and winner flags for user\'s challenge tasks', async () => {
      await groupLeader.post(`/challenges/${challenge._id}/selectWinner/${winningUser._id}`);

      await sleep(0.5);

      const tasks = await winningUser.get('/tasks/user');
      const testTask = _.find(tasks, task => task.text === taskText);

      const updatedUser = await winningUser.sync();
      const challengeTag = updatedUser.tags.find(tags => tags.id === challenge._id);

      expect(testTask.challenge.broken).to.eql('CHALLENGE_CLOSED');
      expect(testTask.challenge.winner).to.eql(winningUser.profile.name);
      expect(challengeTag.challenge).to.eql(false);
    });
  });
});
