/**
 * Impersonation billing mutation blocks — S2-005 / S2-006 regression tests.
 */
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { Hono } from 'hono'
import type { SessionPayload } from '@zync/types'
import type { AppEnv } from '../src/types'

let mockSession: SessionPayload | undefined

vi.mock('../src/middleware/auth', () => ({
  authMiddleware: async (
    c: { set: (key: string, value: unknown) => void },
    next: () => Promise<void>,
  ) => {
    if (mockSession) {
      c.set('session', mockSession)
      c.set('db', {})
    }
    await next()
  },
}))

vi.mock('@zync/auth', () => ({
  requirePermission: () => async (_c: unknown, next: () => Promise<void>) => next(),
  requireAdminSession: () => async (_c: unknown, next: () => Promise<void>) => next(),
  requireTier: () => async (_c: unknown, next: () => Promise<void>) => next(),
}))

vi.mock('@zync/db/queries', () => ({
  createDb: vi.fn(() => ({})),
  updateBillingEmail: vi.fn().mockResolvedValue(undefined),
  logAuditEvent: vi.fn().mockResolvedValue(undefined),
  upsertPaymentGatewayConfig: vi.fn().mockResolvedValue(undefined),
  getPaymentGatewayConfigMasked: vi.fn().mockResolvedValue({
    gateway: 'stripe',
    isActive: true,
    testMode: true,
    configured: true,
  }),
  upsertPaymentGatewaySchema: {
    safeParse: (body: unknown) => {
      const { z } = require('zod')
      return z
        .object({
          gateway: z.enum(['cardcom', 'payplus', 'stripe']),
          isActive: z.boolean().optional().default(false),
          testMode: z.boolean().optional().default(true),
          config: z.record(z.string()),
        })
        .safeParse(body)
    },
  },
}))

import { billingRoutes } from '../src/routes/billing/index'
import { paymentGatewaySettingsRoute } from '../src/routes/settings/payment-gateway'
import { updateBillingEmail, upsertPaymentGatewayConfig } from '@zync/db/queries'

const mockEnv = {
  DB: { connectionString: 'postgresql://test:test@localhost/test' },
  JWT_SECRET: 'test-jwt-secret',
} as AppEnv['Bindings']

const OWNER_SESSION: SessionPayload = {
  sub: 'owner-user-id' as SessionPayload['sub'],
  tid: 'tenant-id' as SessionPayload['tid'],
  role: 'OWNER',
  permissions: ['settings:write'],
  tier: 'business',
  type: 'user',
  v: 1,
  enforce_2fa: false,
  two_factor_verified: true,
  exp: Math.floor(Date.now() / 1000) + 3600,
  iat: Math.floor(Date.now() / 1000),
}

const IMPERSONATION_SESSION: SessionPayload = {
  ...OWNER_SESSION,
  impersonation: true,
  impersonating_admin_id: 'admin-id',
}

const MUTATING_HEADERS = {
  'Content-Type': 'application/json',
  Origin: 'https://app.zync.is',
}

function billingApp() {
  const app = new Hono<AppEnv>()
  app.route('/api/billing', billingRoutes)
  return app
}

function paymentGatewayApp() {
  const app = new Hono<AppEnv>()
  app.route('/api/settings/payment-gateway', paymentGatewaySettingsRoute)
  return app
}

describe('impersonation billing mutation blocks (S2-005 / S2-006)', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockSession = undefined
  })

  it('PATCH /api/billing/email returns 403 during impersonation', async () => {
    mockSession = IMPERSONATION_SESSION

    const res = await billingApp().request(
      '/api/billing/email',
      {
        method: 'PATCH',
        headers: MUTATING_HEADERS,
        body: JSON.stringify({ email: 'attacker@example.com' }),
      },
      mockEnv,
    )

    expect(res.status).toBe(403)
    const body = (await res.json()) as { error: string }
    expect(body.error).toBe('Impersonation sessions cannot perform this action')
    expect(updateBillingEmail).not.toHaveBeenCalled()
  })

  it('PATCH /api/billing/email succeeds for a normal OWNER session', async () => {
    mockSession = OWNER_SESSION

    const res = await billingApp().request(
      '/api/billing/email',
      {
        method: 'PATCH',
        headers: MUTATING_HEADERS,
        body: JSON.stringify({ email: 'billing@example.com' }),
      },
      mockEnv,
    )

    expect(res.status).toBe(200)
    const body = (await res.json()) as { email: string }
    expect(body.email).toBe('billing@example.com')
    expect(updateBillingEmail).toHaveBeenCalledWith({}, 'tenant-id', 'billing@example.com')
  })

  it('PATCH /api/settings/payment-gateway returns 403 during impersonation', async () => {
    mockSession = IMPERSONATION_SESSION

    const res = await paymentGatewayApp().request(
      '/api/settings/payment-gateway',
      {
        method: 'PATCH',
        headers: MUTATING_HEADERS,
        body: JSON.stringify({
          gateway: 'stripe',
          isActive: true,
          testMode: true,
          config: { secret_key: 'sk_test_123' },
        }),
      },
      mockEnv,
    )

    expect(res.status).toBe(403)
    const body = (await res.json()) as { error: string }
    expect(body.error).toBe('Impersonation sessions cannot perform this action')
    expect(upsertPaymentGatewayConfig).not.toHaveBeenCalled()
  })

  it('PATCH /api/settings/payment-gateway succeeds for a normal OWNER session', async () => {
    mockSession = OWNER_SESSION

    const res = await paymentGatewayApp().request(
      '/api/settings/payment-gateway',
      {
        method: 'PATCH',
        headers: MUTATING_HEADERS,
        body: JSON.stringify({
          gateway: 'stripe',
          isActive: true,
          testMode: true,
          config: { secret_key: 'sk_test_123' },
        }),
      },
      mockEnv,
    )

    expect(res.status).toBe(200)
    expect(upsertPaymentGatewayConfig).toHaveBeenCalled()
  })
})
