import moment from 'moment';

import * as sender from '../../../../../../website/server/libs/email';
import api from '../../../../../../website/server/libs/payments/payments';
import { model as User } from '../../../../../../website/server/models/user';
import { model as Group } from '../../../../../../website/server/models/group';
import {
  generateGroup,
} from '../../../../../helpers/api-unit.helper';
import i18n from '../../../../../../website/common/script/i18n';

describe('Canceling a subscription for group', () => {
  let plan; let group; let user; let
    data;

  beforeEach(async () => {
    user = new User();
    user.profile.name = 'sender';
    await user.save();

    group = generateGroup({
      name: 'test group',
      type: 'guild',
      privacy: 'private',
      leader: user._id,
    });
    await group.save();

    user.guilds.push(group._id);
    await user.save();

    data = {
      user,
      sub: {
        key: 'basic_3mo',
      },
      customerId: 'customer-id',
      paymentMethod: 'Payment Method',
      headers: {
        'x-client': 'habitica-web',
        'user-agent': '',
      },
    };

    plan = {
      planId: 'basic_3mo',
      customerId: 'customer-id',
      dateUpdated: new Date(),
      gemsBought: 0,
      paymentMethod: 'paymentMethod',
      extraMonths: 0,
      dateTerminated: null,
      lastBillingDate: new Date(),
      dateCreated: new Date(),
      mysteryItems: [],
      consecutive: {
        trinkets: 0,
        offset: 0,
        gemCapExtra: 0,
      },
    };

    sandbox.stub(sender, 'sendTxn');
  });

  afterEach(() => {
    sender.sendTxn.restore();
  });

  it('adds a month termination date by default', async () => {
    data.groupId = group._id;
    await api.cancelSubscription(data);

    const now = new Date();
    const updatedGroup = await Group.findById(group._id).exec();
    const daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');

    expect(daysTillTermination).to.be.within(29, 30); // 1 month +/- 1 days
  });

  it('adds extraMonths to dateTerminated value', async () => {
    group.purchased.plan.extraMonths = 2;
    await group.save();
    data.groupId = group._id;

    await api.cancelSubscription(data);

    const now = new Date();
    const updatedGroup = await Group.findById(group._id).exec();
    const daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');

    expect(daysTillTermination).to.be.within(89, 90); // 3 months +/- 1 days
  });

  it('handles extra month fractions', async () => {
    group.purchased.plan.extraMonths = 0.3;
    await group.save();
    data.groupId = group._id;

    await api.cancelSubscription(data);

    const now = new Date();
    const updatedGroup = await Group.findById(group._id).exec();
    const daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');

    expect(daysTillTermination).to.be.within(38, 39); // should be about 1 month + 1/3 month
  });

  it('terminates at next billing date if it exists', async () => {
    data.nextBill = moment().add({ days: 15 });
    data.groupId = group._id;

    await api.cancelSubscription(data);

    const now = new Date();
    const updatedGroup = await Group.findById(group._id).exec();
    const daysTillTermination = moment(updatedGroup.purchased.plan.dateTerminated).diff(now, 'days');

    expect(daysTillTermination).to.be.within(13, 15);
  });

  it('resets plan.extraMonths', async () => {
    group.purchased.plan.extraMonths = 5;
    await group.save();
    data.groupId = group._id;

    await api.cancelSubscription(data);

    const updatedGroup = await Group.findById(group._id).exec();
    expect(updatedGroup.purchased.plan.extraMonths).to.eql(0);
  });

  it('sends an email', async () => {
    data.groupId = group._id;
    await api.cancelSubscription(data);

    expect(sender.sendTxn).to.be.calledOnce;
    expect(sender.sendTxn.firstCall.args[0]._id).to.equal(user._id);
    expect(sender.sendTxn.firstCall.args[1]).to.equal('group-cancel-subscription');
    expect(sender.sendTxn.firstCall.args[2]).to.eql([
      { name: 'GROUP_NAME', content: group.name },
    ]);
  });

  it('prevents non group leader from managing subscription', async () => {
    const groupMember = new User();
    groupMember.guilds.push(group._id);
    await groupMember.save();
    data.user = groupMember;
    data.groupId = group._id;

    await expect(api.cancelSubscription(data))
      .eventually.be.rejected.and.to.eql({
        httpCode: 401,
        message: i18n.t('onlyGroupLeaderCanManageSubscription'),
        name: 'NotAuthorized',
      });
  });

  it('allows old group leader to cancel if they created the subscription', async () => {
    data.groupId = group._id;
    data.sub = {
      key: 'group_monthly',
    };
    data.paymentMethod = 'Payment Method';
    await api.createSubscription(data);

    let updatedGroup = await Group.findById(group._id).exec();
    const newLeader = new User();
    newLeader.profile.name = 'newLeader';
    updatedGroup.leader = newLeader._id;
    await newLeader.save();
    await updatedGroup.save();

    await api.cancelSubscription(data);

    updatedGroup = await Group.findById(group._id).exec();

    expect(updatedGroup.purchased.plan.dateTerminated).to.exist;
  });

  it('cancels member subscriptions', async () => {
    data = {
      user,
      sub: {
        key: 'basic_3mo',
      },
      customerId: 'customer-id',
      paymentMethod: 'Payment Method',
      headers: {
        'x-client': 'habitica-web',
        'user-agent': '',
      },
    };
    expect(group.purchased.plan.planId).to.not.exist;
    data.groupId = group._id;
    await api.createSubscription(data);

    await api.cancelSubscription(data);

    const now = new Date();
    now.setHours(0, 0, 0, 0);
    const updatedLeader = await User.findById(user._id).exec();
    const daysTillTermination = moment(updatedLeader.purchased.plan.dateTerminated).diff(now, 'days');
    expect(daysTillTermination).to.be.within(2, 3); // only a few days
  });

  it('sends an email to members of group', async () => {
    const recipient = new User();
    recipient.profile.name = 'recipient';
    recipient.guilds.push(group._id);
    await recipient.save();

    data.groupId = group._id;

    await api.createSubscription(data);
    await api.cancelSubscription(data);

    expect(sender.sendTxn).to.be.have.callCount(6);
    const recipientCall = sender.sendTxn.getCalls().find(call => {
      const isRecipient = call.args[0]._id === recipient._id;
      const isGroupMemberCancel = call.args[1] === 'group-member-cancel';
      return isRecipient && isGroupMemberCancel;
    });
    expect(recipientCall.args[0]._id).to.equal(recipient._id);
    expect(recipientCall.args[1]).to.equal('group-member-cancel');
    expect(recipientCall.args[2]).to.eql([
      { name: 'LEADER', content: user.profile.name },
      { name: 'GROUP_NAME', content: group.name },
    ]);
  });

  it('does not cancel member subscriptions when member does not have a group plan sub (i.e. UNLIMITED_CUSTOMER_ID)', async () => {
    plan.key = 'basic_earned';
    plan.customerId = api.constants.UNLIMITED_CUSTOMER_ID;

    const recipient = new User();
    recipient.profile.name = 'recipient';
    recipient.purchased.plan = plan;
    recipient.guilds.push(group._id);
    await recipient.save();

    data.groupId = group._id;

    await api.cancelSubscription(data);

    const updatedLeader = await User.findById(user._id).exec();
    expect(updatedLeader.purchased.plan.dateTerminated).to.not.exist;
  });

  it('does not cancel a user subscription if they are still in another active group plan', async () => {
    const recipient = new User();
    recipient.profile.name = 'recipient';
    plan.key = 'basic_earned';
    recipient.purchased.plan = plan;
    recipient.guilds.push(group._id);
    await recipient.save();

    data.groupId = group._id;

    await api.createSubscription(data);

    let updatedUser = await User.findById(recipient._id).exec();
    const firstDateCreated = updatedUser.purchased.plan.dateCreated;
    const extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;

    const group2 = generateGroup({
      name: 'test group2',
      type: 'guild',
      privacy: 'private',
      leader: user._id,
    });
    data.groupId = group2._id;
    await group2.save();
    user.guilds.push(group2._id);
    await user.save();
    recipient.guilds.push(group2._id);
    await recipient.save();

    await api.createSubscription(data);

    await api.cancelSubscription(data);

    updatedUser = await User.findById(recipient._id).exec();

    expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto');
    expect(updatedUser.purchased.plan.customerId).to.eql('group-plan');
    expect(updatedUser.purchased.plan.dateUpdated).to.exist;
    expect(updatedUser.purchased.plan.gemsBought).to.eql(0);
    expect(updatedUser.purchased.plan.paymentMethod).to.eql('Group Plan');
    expect(updatedUser.purchased.plan.extraMonths).to.eql(extraMonthsBeforeSecond);
    expect(updatedUser.purchased.plan.dateTerminated).to.eql(null);
    expect(updatedUser.purchased.plan.lastBillingDate).to.not.exist;
    expect(updatedUser.purchased.plan.dateCreated).to.eql(firstDateCreated);
  });

  it('does cancel a leader subscription with two cancelled group plans', async () => {
    data.groupId = group._id;

    await api.createSubscription(data);

    let updatedUser = await User.findById(user._id).exec();
    const firstDateCreated = updatedUser.purchased.plan.dateCreated;
    const extraMonthsBeforeSecond = updatedUser.purchased.plan.extraMonths;

    const group2 = generateGroup({
      name: 'test group2',
      type: 'guild',
      privacy: 'private',
      leader: user._id,
    });
    user.guilds.push(group2._id);
    await user.save();
    data.groupId = group2._id;
    await group2.save();

    await api.createSubscription(data);
    await api.cancelSubscription(data);

    data.groupId = group._id;
    await api.cancelSubscription(data);

    updatedUser = await User.findById(user._id).exec();

    expect(updatedUser.purchased.plan.planId).to.eql('group_plan_auto');
    expect(updatedUser.purchased.plan.customerId).to.eql('group-plan');
    expect(updatedUser.purchased.plan.dateUpdated).to.exist;
    expect(updatedUser.purchased.plan.gemsBought).to.eql(0);
    expect(updatedUser.purchased.plan.paymentMethod).to.eql('Group Plan');
    expect(updatedUser.purchased.plan.extraMonths).to.eql(extraMonthsBeforeSecond);
    expect(updatedUser.purchased.plan.dateTerminated).to.exist;
    expect(updatedUser.purchased.plan.lastBillingDate).to.not.exist;
    expect(updatedUser.purchased.plan.dateCreated).to.eql(firstDateCreated);
  });
});
