import { describe, it, expect, vi } from 'vitest'
import { NewsletterService } from '@/lib/email/newsletter-service'

vi.mock('@/lib/email/tenant-email-service', () => ({
  sendTenantEmail: vi.fn().mockResolvedValue(undefined),
}))

interface MockSubscriber {
  id: number
  email: string
  firstName?: string
  status: 'pending' | 'confirmed' | 'unsubscribed'
  confirmationToken: string
  subscribedAt?: string
  confirmedAt?: string | null
  unsubscribedAt?: string | null
  tenant: number
}

function createMockPayload(subscriber: MockSubscriber) {
  return {
    find: vi.fn().mockImplementation(async ({ where }) => {
      const token = (where as any)?.confirmationToken?.equals
      if (token && subscriber.confirmationToken === token) {
        return { docs: [{ ...subscriber }] }
      }
      return { docs: [] }
    }),
    update: vi.fn().mockImplementation(async ({ data }) => {
      Object.assign(subscriber, data)
      return { ...subscriber }
    }),
    findByID: vi.fn().mockResolvedValue({
      id: subscriber.tenant,
      name: 'Demo Tenant',
      domains: [],
    }),
  }
}

describe('Newsletter unsubscribe security', () => {
  it('rotates token after confirm and again after unsubscribe, blocking replay', async () => {
    const subscriber: MockSubscriber = {
      id: 10,
      email: 'user@example.com',
      status: 'pending',
      confirmationToken: 'confirm-token',
      subscribedAt: new Date().toISOString(),
      tenant: 1,
    }

    const payload = createMockPayload(subscriber)
    const service = new NewsletterService(payload as any)

    const confirmResult = await service.confirmSubscription('confirm-token')
    expect(confirmResult.success).toBe(true)
    expect(subscriber.status).toBe('confirmed')
    expect(subscriber.confirmationToken).not.toBe('confirm-token')

    const unsubscribeToken = subscriber.confirmationToken
    const unsubscribeResult = await service.unsubscribe(unsubscribeToken)
    expect(unsubscribeResult.success).toBe(true)
    expect(subscriber.status).toBe('unsubscribed')
    expect(subscriber.confirmationToken).not.toBe(unsubscribeToken)

    const replayResult = await service.unsubscribe(unsubscribeToken)
    expect(replayResult.success).toBe(false)
  })

  it('does not support predictable ID fallback tokens', async () => {
    const subscriber: MockSubscriber = {
      id: 55,
      email: 'id-test@example.com',
      status: 'confirmed',
      confirmationToken: 'random-token',
      confirmedAt: new Date().toISOString(),
      tenant: 1,
    }

    const payload = createMockPayload(subscriber)
    const service = new NewsletterService(payload as any)

    const result = await service.unsubscribe('55')
    expect(result.success).toBe(false)
  })
})

