import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { NextFunction, Request, Response } from 'express'

vi.mock('../lib/prisma', () => ({
  prisma: {
    subscription: {
      findUnique: vi.fn(),
    },
  },
}))

import { prisma } from '../lib/prisma'
import { requireSubscription } from './requireSubscription'

function responseStub() {
  const res = { status: vi.fn(), json: vi.fn() }
  res.status.mockReturnValue(res)
  res.json.mockReturnValue(res)
  return res as unknown as Response & typeof res
}

describe('requireSubscription middleware', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    process.env.NEXT_PUBLIC_DASHBOARD_URL = 'https://dashboard.example.test'
  })

  it('requires tenant/company context first', async () => {
    const req = {} as Request
    const res = responseStub()
    const next = vi.fn() as NextFunction

    await requireSubscription(req, res, next)

    expect(res.status).toHaveBeenCalledWith(401)
    expect(res.json).toHaveBeenCalledWith({ error: 'unauthenticated', message: 'No company context', statusCode: 401 })
    expect(next).not.toHaveBeenCalled()
  })

  it('blocks suspended companies with a subscription recovery URL', async () => {
    const req = { company: { status: 'SUSPENDED' } } as Request
    const res = responseStub()
    const next = vi.fn() as NextFunction

    await requireSubscription(req, res, next)

    expect(res.status).toHaveBeenCalledWith(402)
    expect(res.json).toHaveBeenCalledWith({
      error: 'subscription_suspended',
      message: 'Your account has been suspended. Please contact support or renew your subscription.',
      statusCode: 402,
      billingUrl: 'https://dashboard.example.test/subscription',
    })
    expect(next).not.toHaveBeenCalled()
    expect(prisma.subscription.findUnique).not.toHaveBeenCalled()
  })

  it('blocks pending companies with setup guidance', async () => {
    const req = { company: { status: 'PENDING' } } as Request
    const res = responseStub()
    const next = vi.fn() as NextFunction

    await requireSubscription(req, res, next)

    expect(res.status).toHaveBeenCalledWith(402)
    expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
      error: 'subscription_pending',
      message: 'Your account is pending activation. Please complete your subscription setup.',
    }))
    expect(next).not.toHaveBeenCalled()
  })

  it('blocks expired trial subscriptions with a subscription recovery URL', async () => {
    vi.mocked(prisma.subscription.findUnique).mockResolvedValue({ status: 'EXPIRED' } as never)

    const req = { company: { id: 'company_1', status: 'TRIALING' } } as Request
    const res = responseStub()
    const next = vi.fn() as NextFunction

    await requireSubscription(req, res, next)

    expect(prisma.subscription.findUnique).toHaveBeenCalledWith({
      where: { companyId: 'company_1' },
      select: { status: true },
    })
    expect(res.status).toHaveBeenCalledWith(402)
    expect(res.json).toHaveBeenCalledWith({
      error: 'subscription_required',
      message: 'Your subscription has ended. Please reactivate to continue.',
      statusCode: 402,
      billingUrl: 'https://dashboard.example.test/subscription',
      subscriptionStatus: 'EXPIRED',
      accessLevel: 'none',
    })
    expect(next).not.toHaveBeenCalled()
  })

  it('allows active companies with active subscriptions through', async () => {
    vi.mocked(prisma.subscription.findUnique).mockResolvedValue({ status: 'ACTIVE' } as never)

    const req = { company: { id: 'company_1', status: 'ACTIVE' } } as Request
    const res = responseStub()
    const next = vi.fn() as NextFunction

    await requireSubscription(req, res, next)

    expect(next).toHaveBeenCalledTimes(1)
    expect(res.status).not.toHaveBeenCalled()
  })

  it('passes subscription lookup failures to Express error handling', async () => {
    const error = new Error('database unavailable')
    vi.mocked(prisma.subscription.findUnique).mockRejectedValue(error as never)

    const req = { company: { id: 'company_1', status: 'ACTIVE' } } as Request
    const res = responseStub()
    const next = vi.fn() as NextFunction

    await requireSubscription(req, res, next)

    expect(next).toHaveBeenCalledWith(error)
    expect(res.status).not.toHaveBeenCalled()
  })
})
