import { beforeEach, describe, expect, it, vi } from 'vitest'

vi.mock('../../lib/prisma', () => ({ prisma: {} }))
vi.mock('../../lib/redis', () => ({
  redis: { on: vi.fn(), get: vi.fn(), set: vi.fn(), del: vi.fn(), quit: vi.fn() },
}))
vi.mock('../../middleware/requireCompanyAuth', () => ({
  requireCompanyDocumentAuth: (_req: any, _res: any, next: any) => next(),
  requireCompanyAuth: (req: any, _res: any, next: any) => {
    req.employee = { id: 'employee_owner', role: 'OWNER', firstName: 'Aya', lastName: 'Owner' }
    next()
  },
}))
vi.mock('../../middleware/requireTenant', () => ({
  requireTenant: (req: any, _res: any, next: any) => {
    req.companyId = 'company_1'
    next()
  },
}))
vi.mock('../../middleware/requireSubscription', () => ({ requireSubscription: (_req: any, _res: any, next: any) => next() }))
vi.mock('../../middleware/requireRole', () => ({ requireRole: () => (_req: any, _res: any, next: any) => next() }))
vi.mock('../../modules/subscriptions/subscription.service', () => ({
  getPlans: vi.fn(),
  getProviders: vi.fn(),
  getPlanFeatures: vi.fn(),
  getSubscription: vi.fn(),
  getInvoices: vi.fn(),
  getEvents: vi.fn(),
  getEntitlement: vi.fn(),
  startTrial: vi.fn(),
  checkout: vi.fn(),
  reactivate: vi.fn(),
  changePlan: vi.fn(),
  cancel: vi.fn(),
  resume: vi.fn(),
  capturePaypal: vi.fn(),
  handleAmanpayWebhook: vi.fn(),
  handlePaypalWebhook: vi.fn(),
}))
vi.mock('../../modules/team/team.service', () => ({
  getMembers: vi.fn(),
  getMemberStats: vi.fn(),
  inviteEmployee: vi.fn(),
  updateEmployeeRole: vi.fn(),
  deactivateEmployee: vi.fn(),
  reactivateEmployee: vi.fn(),
  removeEmployee: vi.fn(),
}))

import request from 'supertest'
import { createApp } from '../../app'
import * as subscriptionService from '../../modules/subscriptions/subscription.service'
import * as teamService from '../../modules/team/team.service'

const app = createApp()

describe('subscription and team API validation contracts', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(subscriptionService.startTrial).mockResolvedValue({ id: 'sub_trial' } as never)
    vi.mocked(subscriptionService.checkout).mockResolvedValue({ invoice: { id: 'invoice_1' }, checkoutUrl: 'https://pay.example.test' } as never)
    vi.mocked(subscriptionService.cancel).mockResolvedValue({ id: 'sub_1', cancelAtPeriodEnd: true } as never)
    vi.mocked(subscriptionService.capturePaypal).mockResolvedValue({ success: true } as never)
    vi.mocked(teamService.inviteEmployee).mockResolvedValue({ id: 'employee_2' } as never)
    vi.mocked(teamService.updateEmployeeRole).mockResolvedValue({ id: 'employee_2', role: 'MANAGER' } as never)
  })

  it('defaults trial currency and forwards company context', async () => {
    const res = await request(app).post('/api/v1/subscriptions/trial').send({ plan: 'STARTER', billingPeriod: 'MONTHLY' })

    expect(res.status).toBe(200)
    expect(subscriptionService.startTrial).toHaveBeenCalledWith('company_1', 'STARTER', 'MONTHLY', 'MAD')
  })

  it('rejects checkout payloads with unsupported currency before service execution', async () => {
    const res = await request(app).post('/api/v1/subscriptions/checkout').send({
      plan: 'PRO',
      billingPeriod: 'ANNUAL',
      currency: 'USD',
      provider: 'PAYPAL',
      successUrl: 'https://app.example.test/success',
      failureUrl: 'https://app.example.test/failure',
    })

    expect(res.status).toBe(400)
    expect(subscriptionService.checkout).not.toHaveBeenCalled()
  })

  it('defaults cancellation mode at the route boundary', async () => {
    const res = await request(app).post('/api/v1/subscriptions/cancel').send({ reason: 'not needed' })

    expect(res.status).toBe(200)
    expect(subscriptionService.cancel).toHaveBeenCalledWith('company_1', 'period_end', 'not needed')
  })

  it('requires PayPal order id for capture before service execution', async () => {
    const res = await request(app).post('/api/v1/subscriptions/capture-paypal').send({})

    expect(res.status).toBe(400)
    expect(subscriptionService.capturePaypal).not.toHaveBeenCalled()
  })

  it('normalizes team invites through validation before service execution', async () => {
    const res = await request(app).post('/api/v1/team/invite').send({
      firstName: 'Aya',
      lastName: 'Agent',
      email: 'agent@example.test',
      role: 'AGENT',
    })

    expect(res.status).toBe(201)
    expect(teamService.inviteEmployee).toHaveBeenCalledWith('company_1', 'employee_owner', {
      firstName: 'Aya',
      lastName: 'Agent',
      email: 'agent@example.test',
      role: 'AGENT',
    })
  })

  it('rejects owner role assignment before team service execution', async () => {
    const res = await request(app).patch('/api/v1/team/employee_2/role').send({ role: 'OWNER' })

    expect(res.status).toBe(400)
    expect(teamService.updateEmployeeRole).not.toHaveBeenCalled()
  })
})
