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

vi.mock('../../lib/prisma', () => ({
  prisma: {
    subscription: {
      findUnique: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      upsert: vi.fn(),
      findMany: vi.fn(),
    },
    subscriptionInvoice: {
      create: vi.fn(),
      update: vi.fn(),
      findMany: vi.fn(),
      findFirst: vi.fn(),
      findFirstOrThrow: vi.fn(),
      findUniqueOrThrow: vi.fn(),
    },
    paymentAttempt: { create: vi.fn(), findMany: vi.fn() },
    subscriptionEvent: { create: vi.fn(), findMany: vi.fn() },
  },
}))

import { prisma } from '../../lib/prisma'
import * as repo from './subscription.repo'

describe('subscription.repo edge queries and mutations', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.useFakeTimers()
    vi.setSystemTime(new Date('2026-06-01T12:00:00.000Z'))
  })

  afterEach(() => vi.useRealTimers())

  it('returns existing subscriptions without creating duplicates', async () => {
    const existing = { id: 'sub_1', companyId: 'company_1' }
    vi.mocked(prisma.subscription.findUnique).mockResolvedValue(existing as never)

    const result = await repo.findOrCreateSubscription('company_1', 'PRO', 'ANNUAL', 'MAD')

    expect(result).toBe(existing)
    expect(prisma.subscription.create).not.toHaveBeenCalled()
  })

  it('creates new subscriptions in payment-pending state when none exists', async () => {
    vi.mocked(prisma.subscription.findUnique).mockResolvedValue(null as never)
    vi.mocked(prisma.subscription.create).mockResolvedValue({ id: 'sub_2' } as never)

    await repo.findOrCreateSubscription('company_1', 'GROWTH', 'MONTHLY', 'MAD')

    expect(prisma.subscription.create).toHaveBeenCalledWith({
      data: {
        companyId: 'company_1',
        plan: 'GROWTH',
        billingPeriod: 'MONTHLY',
        currency: 'MAD',
        status: 'PAYMENT_PENDING',
      },
    })
  })

  it('sets payment pending due dates seven days from the mutation time', async () => {
    await repo.setPaymentPending('sub_1')

    expect(prisma.subscription.update).toHaveBeenCalledWith({
      where: { id: 'sub_1' },
      data: {
        status: 'PAYMENT_PENDING',
        paymentPendingSince: new Date('2026-06-01T12:00:00.000Z'),
        paymentDueAt: new Date('2026-06-08T12:00:00.000Z'),
      },
    })
  })

  it('distinguishes immediate cancellation from period-end cancellation', async () => {
    await repo.setCancelled('sub_1', true)
    expect(prisma.subscription.update).toHaveBeenLastCalledWith({
      where: { id: 'sub_1' },
      data: {
        status: 'CANCELLED',
        cancelledAt: new Date('2026-06-01T12:00:00.000Z'),
        endedAt: new Date('2026-06-01T12:00:00.000Z'),
      },
    })

    await repo.setCancelled('sub_1', false)
    expect(prisma.subscription.update).toHaveBeenLastCalledWith({
      where: { id: 'sub_1' },
      data: { cancelAtPeriodEnd: true },
    })
  })

  it('queries scheduled job candidates with status-specific cutoffs', async () => {
    const cutoff = new Date('2026-05-25T12:00:00.000Z')

    await repo.findPaymentPendingTimedOut(cutoff)
    expect(prisma.subscription.findMany).toHaveBeenLastCalledWith({
      where: { status: 'PAYMENT_PENDING', paymentPendingSince: { lte: cutoff } },
      include: { company: true },
    })

    await repo.findPastDueTimedOut(cutoff)
    expect(prisma.subscription.findMany).toHaveBeenLastCalledWith({
      where: { status: 'PAST_DUE', pastDueSince: { lte: cutoff } },
      include: { company: true },
    })

    await repo.findSubscriptionsToExpireAtPeriodEnd()
    expect(prisma.subscription.findMany).toHaveBeenLastCalledWith({
      where: {
        status: 'ACTIVE',
        cancelAtPeriodEnd: true,
        currentPeriodEnd: { lte: new Date('2026-06-01T12:00:00.000Z') },
      },
      include: { company: true },
    })
  })

  it('creates subscription events with system source and current occurrence time by default', async () => {
    await repo.createEvent({
      subscriptionId: 'sub_1',
      companyId: 'company_1',
      eventType: 'trial.expired',
      payload: { trialEndAt: '2026-06-01' },
    })

    expect(prisma.subscriptionEvent.create).toHaveBeenCalledWith({
      data: {
        subscriptionId: 'sub_1',
        companyId: 'company_1',
        eventType: 'trial.expired',
        payload: { trialEndAt: '2026-06-01' },
        source: 'system',
        occurredAt: new Date('2026-06-01T12:00:00.000Z'),
      },
    })
  })
})
