import { vi, expect } from 'vitest'
import sinon from 'sinon'

import mongodb from 'mongodb-legacy'
import Errors from '../../../../app/src/Features/Errors/Errors.js'
const modulePath =
  '../../../../app/src/Features/Subscription/TeamInvitesHandler'

vi.mock('../../../../app/src/Features/Errors/Errors.js', () =>
  vi.importActual('../../../../app/src/Features/Errors/Errors.js')
)

const { ObjectId } = mongodb

describe('TeamInvitesHandler', function () {
  beforeEach(async function (ctx) {
    ctx.manager = {
      _id: '666666',
      first_name: 'Daenerys',
      last_name: 'Targaryen',
      email: 'daenerys@example.com',
      emails: [{ email: 'daenerys@example.com' }],
    }

    ctx.token = 'aaaaaaaaaaaaaaaaaaaaaa'

    ctx.teamInvite = {
      email: 'jorah@example.com',
      token: ctx.token,
    }
    // ensure teamInvite can be converted from Document to Object
    ctx.teamInvite.toObject = () => ctx.teamInvite

    ctx.subscription = {
      id: '55153a8014829a865bbf700d',
      _id: new ObjectId('55153a8014829a865bbf700d'),
      recurlySubscription_id: '1a2b3c4d5e6f7g',
      admin_id: ctx.manager._id,
      groupPlan: true,
      member_ids: [],
      teamInvites: [ctx.teamInvite],
      save: sinon.stub().resolves(),
    }

    ctx.SubscriptionLocator = {
      promises: {
        getUsersSubscription: sinon.stub(),
        getSubscription: sinon.stub().resolves(ctx.subscription),
      },
    }

    ctx.UserGetter = {
      promises: {
        getUser: sinon.stub().resolves(),
        getUserByAnyEmail: sinon.stub().resolves(),
        getUserByMainEmail: sinon.stub().resolves(),
      },
    }

    ctx.SubscriptionUpdater = {
      promises: {
        addUserToGroup: sinon.stub().resolves(),
        deleteSubscription: sinon.stub().resolves(),
      },
    }

    ctx.LimitationsManager = {
      teamHasReachedMemberLimit: sinon.stub().returns(false),
    }

    ctx.Subscription = {
      findOne: sinon.stub().resolves(),
      updateOne: sinon.stub().resolves(),
    }

    ctx.SSOConfig = {
      findById: sinon.stub().resolves(),
    }

    ctx.EmailHandler = {
      promises: {
        sendEmail: sinon.stub().resolves(null),
      },
    }

    ctx.newToken = 'bbbbbbbbb'

    ctx.crypto = {
      randomBytes: () => {
        return { toString: sinon.stub().returns(ctx.newToken) }
      },
    }

    ctx.UserGetter.promises.getUser
      .withArgs(ctx.manager._id)
      .resolves(ctx.manager)
    ctx.UserGetter.promises.getUserByAnyEmail
      .withArgs(ctx.manager.email)
      .resolves(ctx.manager)
    ctx.UserGetter.promises.getUserByMainEmail
      .withArgs(ctx.manager.email)
      .resolves(ctx.manager)

    ctx.SubscriptionLocator.promises.getUsersSubscription.resolves(
      ctx.subscription
    )

    ctx.NotificationsBuilder = {
      promises: {
        groupInvitation: sinon.stub().returns({
          create: sinon.stub().resolves(),
          read: sinon.stub().resolves(),
        }),
      },
    }

    ctx.Subscription.findOne.resolves(ctx.subscription)

    ctx.RecurlyClient = {
      promises: {
        terminateSubscriptionByUuid: sinon.stub().resolves(),
      },
    }

    vi.doMock('mongodb-legacy', () => ({
      default: { ObjectId },
    }))

    vi.doMock('crypto', () => ({
      default: ctx.crypto,
    }))

    vi.doMock('@overleaf/settings', () => ({
      default: { siteUrl: 'http://example.com', appName: 'Overleaf' },
    }))

    vi.doMock('../../../../app/src/models/TeamInvite', () => ({
      TeamInvite: (ctx.TeamInvite = {}),
    }))

    vi.doMock('../../../../app/src/models/Subscription', () => ({
      Subscription: ctx.Subscription,
    }))

    vi.doMock('../../../../app/src/models/SSOConfig', () => ({
      SSOConfig: ctx.SSOConfig,
    }))

    vi.doMock('../../../../app/src/Features/User/UserGetter', () => ({
      default: ctx.UserGetter,
    }))

    vi.doMock(
      '../../../../app/src/Features/Subscription/SubscriptionLocator',
      () => ({
        default: ctx.SubscriptionLocator,
      })
    )

    vi.doMock(
      '../../../../app/src/Features/Subscription/SubscriptionUpdater',
      () => ({
        default: ctx.SubscriptionUpdater,
      })
    )

    vi.doMock(
      '../../../../app/src/Features/Subscription/LimitationsManager',
      () => ({
        default: ctx.LimitationsManager,
      })
    )

    vi.doMock('../../../../app/src/Features/Email/EmailHandler', () => ({
      default: ctx.EmailHandler,
    }))

    vi.doMock(
      '../../../../app/src/Features/Notifications/NotificationsBuilder',
      () => ({
        default: ctx.NotificationsBuilder,
      })
    )

    vi.doMock('../../../../app/src/infrastructure/Modules', () => ({
      default: (ctx.Modules = {
        promises: { hooks: { fire: sinon.stub().resolves() } },
      }),
    }))

    vi.doMock(
      '../../../../app/src/Features/Subscription/RecurlyClient',
      () => ({
        default: ctx.RecurlyClient,
      })
    )

    ctx.TeamInvitesHandler = (await import(modulePath)).default
  })

  describe('getInvite', function () {
    it("returns the invite if there's one", async function (ctx) {
      const { invite, subscription } =
        await ctx.TeamInvitesHandler.promises.getInvite(ctx.token)

      expect(invite).to.deep.eq(ctx.teamInvite)
      expect(subscription).to.deep.eq(ctx.subscription)
    })

    it("returns teamNotFound if there's none", async function (ctx) {
      ctx.Subscription.findOne = sinon.stub().resolves(null)

      let error
      try {
        await ctx.TeamInvitesHandler.promises.getInvite(ctx.token)
      } catch (err) {
        error = err
      }

      expect(error).to.be.instanceOf(Errors.NotFoundError)
    })
  })

  describe('createInvite', function () {
    it('adds the team invite to the subscription', async function (ctx) {
      const invite = await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        'John.Snow@example.com'
      )
      expect(invite.token).to.eq(ctx.newToken)
      expect(invite.email).to.eq('john.snow@example.com')
      expect(invite.inviterName).to.eq(
        'Daenerys Targaryen (daenerys@example.com)'
      )
      expect(invite.invite).to.be.true
      expect(ctx.subscription.teamInvites).to.deep.include(invite)
    })

    it('sends an email', async function (ctx) {
      await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        'John.Snow@example.com'
      )

      ctx.EmailHandler.promises.sendEmail
        .calledWith(
          'verifyEmailToJoinTeam',
          sinon.match({
            to: 'john.snow@example.com',
            inviter: ctx.manager,
            acceptInviteUrl: `http://example.com/subscription/invites/${ctx.newToken}/`,
          })
        )
        .should.equal(true)
    })

    it('refreshes the existing invite if the email has already been invited', async function (ctx) {
      const originalInvite = Object.assign({}, ctx.teamInvite)

      const invite = await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        originalInvite.email
      )
      expect(invite).to.exist

      expect(ctx.subscription.teamInvites.length).to.eq(1)
      expect(ctx.subscription.teamInvites).to.deep.include(invite)

      expect(invite.email).to.eq(originalInvite.email)

      ctx.subscription.save.calledOnce.should.eq(true)
    })

    it('removes any legacy invite from the subscription', async function (ctx) {
      await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        'John.Snow@example.com'
      )

      ctx.Subscription.updateOne
        .calledWith(
          { _id: new ObjectId('55153a8014829a865bbf700d') },
          { $pull: { invited_emails: 'john.snow@example.com' } }
        )
        .should.eq(true)
    })

    it('add user to subscription if inviting self', async function (ctx) {
      const invite = await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        ctx.manager.email
      )
      sinon.assert.calledWith(
        ctx.SubscriptionUpdater.promises.addUserToGroup,
        ctx.subscription._id,
        ctx.manager._id
      )
      sinon.assert.notCalled(ctx.subscription.save)
      expect(invite.token).to.not.exist
      expect(invite.email).to.eq(ctx.manager.email)
      expect(invite.first_name).to.eq(ctx.manager.first_name)
      expect(invite.last_name).to.eq(ctx.manager.last_name)
      expect(invite.invite).to.be.false
    })

    it('sends an SSO invite if SSO is enabled and inviting self', async function (ctx) {
      ctx.subscription.ssoConfig = new ObjectId('abc123abc123abc123abc123')
      ctx.SSOConfig.findById
        .withArgs(ctx.subscription.ssoConfig)
        .resolves({ enabled: true })

      await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        ctx.manager.email
      )
      sinon.assert.calledWith(
        ctx.Modules.promises.hooks.fire,
        'sendGroupSSOReminder',
        ctx.manager._id,
        ctx.subscription._id
      )
    })

    it('does not send an SSO invite if SSO is disabled and inviting self', async function (ctx) {
      ctx.subscription.ssoConfig = new ObjectId('abc123abc123abc123abc123')
      ctx.SSOConfig.findById
        .withArgs(ctx.subscription.ssoConfig)
        .resolves({ enabled: false })

      await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        ctx.manager.email
      )
      sinon.assert.notCalled(ctx.Modules.promises.hooks.fire)
    })

    it('sends a notification if inviting registered user', async function (ctx) {
      const id = new ObjectId('6a6b3a8014829a865bbf700d')
      const managedUsersEnabled = false

      ctx.UserGetter.promises.getUserByMainEmail
        .withArgs('john.snow@example.com')
        .resolves({
          _id: id,
        })

      const invite = await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        'John.Snow@example.com'
      )
      ctx.NotificationsBuilder.promises
        .groupInvitation(
          id.toString(),
          ctx.subscription._id,
          managedUsersEnabled
        )
        .create.calledWith(invite)
        .should.eq(true)
    })

    it('creates an audit log entry for group-invite-sent for managed subscription', async function (ctx) {
      ctx.subscription.managedUsersEnabled = true

      const auditLog = {
        initiatorId: ctx.manager._id,
        ipAddress: '192.0.2.1',
      }

      await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        'John.Snow@example.com',
        auditLog
      )

      sinon.assert.calledWith(
        ctx.Modules.promises.hooks.fire,
        'addGroupAuditLogEntry',
        sinon.match({
          initiatorId: auditLog.initiatorId,
          ipAddress: auditLog.ipAddress,
          groupId: ctx.subscription._id,
          operation: 'group-invite-sent',
          info: { invitedEmail: 'john.snow@example.com' },
        })
      )
    })

    it('does not create an audit log entry for non-managed subscription', async function (ctx) {
      ctx.subscription.managedUsersEnabled = false

      const auditLog = {
        initiatorId: ctx.manager._id,
        ipAddress: '192.0.2.1',
      }

      await ctx.TeamInvitesHandler.promises.createInvite(
        ctx.manager._id,
        ctx.subscription,
        'John.Snow@example.com',
        auditLog
      )

      sinon.assert.neverCalledWith(
        ctx.Modules.promises.hooks.fire,
        'addGroupAuditLogEntry'
      )
    })

    describe('when domain capture is enabled', function () {
      it('creates a domain capture invite', async function (ctx) {
        const initPath = '/saml/ukamf/init?group_id=12345'
        ctx.Modules.promises.hooks.fire.resolves([initPath])
        ctx.UserGetter.promises.getUser.resolves(ctx.manager)

        ctx.subscription.domainCaptureEnabled = true
        const invite = await ctx.TeamInvitesHandler.promises.createInvite(
          ctx.manager._id,
          ctx.subscription,
          'user@example.com',
          { domainCapture: true }
        )
        expect(invite.token).to.be.undefined
        expect(invite.domainCapture).to.be.true
        expect(invite.email).to.eq('user@example.com')

        sinon.assert.calledWith(
          ctx.Modules.promises.hooks.fire,
          'getGroupSSOInitPath',
          ctx.subscription,
          invite.email
        )

        ctx.EmailHandler.promises.sendEmail
          .calledWith(
            'verifyEmailToJoinTeam',
            sinon.match({
              to: 'user@example.com',
              inviter: ctx.manager,
              acceptInviteUrl: `http://example.com${initPath}`,
              appName: 'Overleaf',
            })
          )
          .should.equal(true)
      })

      describe('when managed users is also enabled', function () {
        it('creates a domain capture invite', async function (ctx) {
          ctx.SubscriptionLocator.promises.getAdminEmailAndName = sinon
            .stub()
            .resolves(ctx.manager)

          ctx.UserGetter.promises.getUserByAnyEmail
            .withArgs('user@example.com')
            .resolves(ctx.user)
          const initPath = '/saml/ukamf/init?group_id=12345'
          ctx.Modules.promises.hooks.fire.resolves([initPath])
          ctx.UserGetter.promises.getUser.resolves(ctx.manager)
          ctx.subscription.managedUsersEnabled = true
          ctx.subscription.domainCaptureEnabled = true
          const invite = await ctx.TeamInvitesHandler.promises.createInvite(
            ctx.manager._id,
            ctx.subscription,
            'user@example.com',
            { domainCapture: true }
          )
          expect(invite.token).to.be.undefined
          expect(invite.domainCapture).to.be.true
          expect(invite.email).to.eq('user@example.com')

          sinon.assert.calledWith(
            ctx.Modules.promises.hooks.fire,
            'getGroupSSOInitPath',
            ctx.subscription,
            invite.email
          )

          ctx.EmailHandler.promises.sendEmail
            .calledWith(
              'inviteNewUserToJoinManagedUsers',
              sinon.match({
                to: 'user@example.com',
                inviter: ctx.manager,
                acceptInviteUrl: `http://example.com${initPath}`,
                appName: 'Overleaf',
                admin: ctx.manager,
              })
            )
            .should.equal(true)
        })
      })
    })

    describe('when invite exists without token (created with domain capture) and domain capture is now disabled', function () {
      it('generates a token for the existing invite', async function (ctx) {
        const existingInvite = {
          email: 'user@example.com',
          inviterName: 'Daenerys Targaryen (daenerys@example.com)',
          domainCapture: true,
          sentAt: new Date(),
        }
        ctx.subscription.teamInvites = [existingInvite]
        ctx.subscription.domainCaptureEnabled = false

        const invite = await ctx.TeamInvitesHandler.promises.createInvite(
          ctx.manager._id,
          ctx.subscription,
          'user@example.com'
        )

        expect(invite.token).to.eq(ctx.newToken)
        expect(invite.domainCapture).to.be.false
        expect(invite.email).to.eq('user@example.com')

        sinon.assert.calledOnce(ctx.subscription.save)

        ctx.EmailHandler.promises.sendEmail
          .calledWith(
            'verifyEmailToJoinTeam',
            sinon.match({
              to: 'user@example.com',
              inviter: ctx.manager,
              acceptInviteUrl: `http://example.com/subscription/invites/${ctx.newToken}/`,
            })
          )
          .should.equal(true)
      })
    })
  })

  describe('importInvite', function () {
    beforeEach(function (ctx) {
      ctx.sentAt = new Date()
    })

    it('can imports an invite from v1', function (ctx) {
      ctx.TeamInvitesHandler.importInvite(
        ctx.subscription,
        'A-Team',
        'hannibal@a-team.org',
        'secret',
        ctx.sentAt,
        error => {
          expect(error).not.to.exist

          ctx.subscription.save.calledOnce.should.eq(true)

          const invite = ctx.subscription.teamInvites.find(
            i => i.email === 'hannibal@a-team.org'
          )
          expect(invite.token).to.eq('secret')
          expect(invite.sentAt).to.eq(ctx.sentAt)
        }
      )
    })
  })

  describe('acceptInvite', function () {
    beforeEach(function (ctx) {
      ctx.user = {
        id: '123456789',
        first_name: 'Tyrion',
        last_name: 'Lannister',
        email: 'tyrion@example.com',
      }

      ctx.user_subscription = {
        id: '66264b9125930b976cc0811e',
        _id: new ObjectId('66264b9125930b976cc0811e'),
        groupPlan: false,
        recurlySubscription_id: 'fa1b2cfa156gh',
        admin_id: '123456789',
        member_ids: [],
        teamInvites: [],
        save: sinon.stub().resolves(),
      }

      ctx.ipAddress = '127.0.0.1'

      ctx.UserGetter.promises.getUserByAnyEmail
        .withArgs(ctx.user.email)
        .resolves(ctx.user)

      ctx.SubscriptionLocator.promises.getUsersSubscription
        .withArgs(ctx.user.id)
        .resolves(ctx.user_subscription)

      ctx.subscription.teamInvites.push({
        email: 'john.snow@example.com',
        token: 'dddddddd',
        inviterName: 'Daenerys Targaryen (daenerys@example.com)',
      })
    })

    describe('with standard group', function () {
      it('adds the user to the team', async function (ctx) {
        await ctx.TeamInvitesHandler.promises.acceptInvite(
          'dddddddd',
          ctx.user.id,
          ctx.ipAddress
        )
        ctx.SubscriptionUpdater.promises.addUserToGroup
          .calledWith(ctx.subscription._id, ctx.user.id)
          .should.eq(true)
      })

      it('removes the invite from the subscription', async function (ctx) {
        await ctx.TeamInvitesHandler.promises.acceptInvite(
          'dddddddd',
          ctx.user.id,
          ctx.ipAddress
        )
        ctx.Subscription.updateOne
          .calledWith(
            { _id: new ObjectId('55153a8014829a865bbf700d') },
            { $pull: { teamInvites: { email: 'john.snow@example.com' } } }
          )
          .should.eq(true)
      })

      it('removes dashboard notification after they accepted group invitation', async function (ctx) {
        const managedUsersEnabled = false

        await ctx.TeamInvitesHandler.promises.acceptInvite(
          'dddddddd',
          ctx.user.id,
          ctx.ipAddress
        )
        sinon.assert.called(
          ctx.NotificationsBuilder.promises.groupInvitation(
            ctx.user.id,
            ctx.subscription._id,
            managedUsersEnabled
          ).read
        )
      })

      it('should not schedule an SSO invite reminder', async function (ctx) {
        await ctx.TeamInvitesHandler.promises.acceptInvite(
          'dddddddd',
          ctx.user.id,
          ctx.ipAddress
        )
        sinon.assert.notCalled(ctx.Modules.promises.hooks.fire)
      })
    })

    describe('with managed group', function () {
      it('should enroll the group member', async function (ctx) {
        ctx.subscription.managedUsersEnabled = true

        await ctx.TeamInvitesHandler.promises.acceptInvite(
          'dddddddd',
          ctx.user.id,
          ctx.ipAddress
        )
        sinon.assert.calledWith(
          ctx.SubscriptionUpdater.promises.deleteSubscription,
          ctx.user_subscription,
          { id: ctx.user.id, ip: ctx.ipAddress }
        )
        sinon.assert.calledWith(
          ctx.Modules.promises.hooks.fire,
          'terminateSubscription',
          ctx.user_subscription
        )
        sinon.assert.calledWith(
          ctx.Modules.promises.hooks.fire,
          'enrollInManagedSubscription',
          ctx.user.id,
          ctx.subscription
        )
      })

      it('should not delete the users subscription if that subscription is also the join target', async function (ctx) {
        ctx.subscription.managedUsersEnabled = true
        ctx.SubscriptionLocator.promises.getUsersSubscription
          .withArgs(ctx.user.id)
          .resolves(ctx.subscription)

        await ctx.TeamInvitesHandler.promises.acceptInvite(
          'dddddddd',
          ctx.user.id,
          ctx.ipAddress
        )

        sinon.assert.notCalled(
          ctx.SubscriptionUpdater.promises.deleteSubscription
        )
      })
    })

    describe('with group SSO enabled', function () {
      it('should schedule an SSO invite reminder', async function (ctx) {
        ctx.subscription.ssoConfig = 'ssoconfig1'
        ctx.SSOConfig.findById
          .withArgs('ssoconfig1')
          .resolves({ enabled: true })

        await ctx.TeamInvitesHandler.promises.acceptInvite(
          'dddddddd',
          ctx.user.id,
          ctx.ipAddress
        )
        sinon.assert.calledWith(
          ctx.Modules.promises.hooks.fire,
          'scheduleGroupSSOReminder',
          ctx.user.id,
          ctx.subscription._id
        )
      })
    })
  })

  describe('revokeInvite', function () {
    it('removes the team invite from the subscription', async function (ctx) {
      await ctx.TeamInvitesHandler.promises.revokeInvite(
        ctx.manager._id,
        ctx.subscription,
        'jorah@example.com'
      )
      ctx.Subscription.updateOne
        .calledWith(
          { _id: new ObjectId('55153a8014829a865bbf700d') },
          { $pull: { teamInvites: { email: 'jorah@example.com' } } }
        )
        .should.eq(true)

      ctx.Subscription.updateOne
        .calledWith(
          { _id: new ObjectId('55153a8014829a865bbf700d') },
          { $pull: { invited_emails: 'jorah@example.com' } }
        )
        .should.eq(true)
    })

    it('removes dashboard notification for pending group invitation', async function (ctx) {
      const managedUsersEnabled = false

      const pendingUser = {
        id: '1a2b',
        email: 'tyrion@example.com',
      }

      ctx.UserGetter.promises.getUserByAnyEmail
        .withArgs(pendingUser.email)
        .resolves(pendingUser)

      await ctx.TeamInvitesHandler.promises.revokeInvite(
        ctx.manager._id,
        ctx.subscription,
        pendingUser.email
      )

      sinon.assert.called(
        ctx.NotificationsBuilder.promises.groupInvitation(
          pendingUser.id,
          ctx.subscription._id,
          managedUsersEnabled
        ).read
      )
    })
  })

  describe('createTeamInvitesForLegacyInvitedEmail', function () {
    beforeEach(function (ctx) {
      ctx.subscription.invited_emails = [
        'eddard@example.com',
        'robert@example.com',
      ]
      ctx.TeamInvitesHandler.createInvite = sinon.stub().resolves(null)
      ctx.SubscriptionLocator.promises.getGroupsWithEmailInvite = sinon
        .stub()
        .resolves([ctx.subscription])
    })

    it('sends an invitation email to addresses in the legacy invited_emails field', async function (ctx) {
      const invites =
        await ctx.TeamInvitesHandler.promises.createTeamInvitesForLegacyInvitedEmail(
          'eddard@example.com'
        )

      expect(invites.length).to.eq(1)

      const [invite] = invites
      expect(invite.token).to.eq(ctx.newToken)
      expect(invite.email).to.eq('eddard@example.com')
      expect(invite.inviterName).to.eq(
        'Daenerys Targaryen (daenerys@example.com)'
      )
      expect(invite.invite).to.be.true
      expect(ctx.subscription.teamInvites).to.deep.include(invite)
    })
  })

  describe('validation', function () {
    it("doesn't create an invite if the team limit has been reached", async function (ctx) {
      ctx.LimitationsManager.teamHasReachedMemberLimit = sinon
        .stub()
        .returns(true)
      let error

      try {
        await ctx.TeamInvitesHandler.promises.createInvite(
          ctx.manager._id,
          ctx.subscription,
          'John.Snow@example.com'
        )
      } catch (err) {
        error = err
      }

      expect(error).to.exist

      expect(error).to.deep.equal({
        limitReached: true,
      })
    })

    it("doesn't create an invite if the subscription is not in a group plan", async function (ctx) {
      ctx.subscription.groupPlan = false
      let error

      try {
        await ctx.TeamInvitesHandler.promises.createInvite(
          ctx.manager._id,
          ctx.subscription,
          'John.Snow@example.com'
        )
      } catch (err) {
        error = err
      }

      expect(error).to.exist

      expect(error).to.deep.equal({
        wrongPlan: true,
      })
    })

    it("doesn't create an invite if the user is already part of the team", async function (ctx) {
      const member = {
        id: '1a2b',
        _id: '1a2b',
        email: 'tyrion@example.com',
      }

      ctx.subscription.member_ids = [member.id]
      ctx.UserGetter.promises.getUserByAnyEmail
        .withArgs(member.email)
        .resolves(member)

      let error

      try {
        await ctx.TeamInvitesHandler.promises.createInvite(
          ctx.manager._id,
          ctx.subscription,
          'tyrion@example.com'
        )
      } catch (err) {
        error = err
      }

      expect(error).to.exist

      expect(error).to.eql({
        alreadyInTeam: true,
      })
    })
  })
})
