// @vitest-environment happy-dom
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'

/**
 * Exercises the per-user MFA enrollment loop in AccountSecurity against a
 * mocked auth API:
 *   GET  /auth/me           → current totpEnabled state
 *   POST /auth/totp/setup   → provisioning URI + one-time backup codes
 *   POST /auth/totp/verify  → flips MFA on
 *   POST /auth/totp/disable → turns MFA off
 */
interface ApiCall {
  endpoint: string
  options?: { method?: string; headers?: Record<string, string>; body?: unknown }
}

let totpEnabled = false
const calls: ApiCall[] = []

const cmsApi = vi.fn(async (endpoint: string, options?: ApiCall['options']) => {
  calls.push({ endpoint, options })
  const method = options?.method ?? 'GET'
  if (endpoint === '/auth/me') return { data: { totpEnabled }, status: 200 }
  if (endpoint === '/auth/totp/setup' && method === 'POST') {
    const pw = options?.headers?.['x-reauth-password']
    if (!pw || pw === 'wrong') return { error: 'Re-authentication failed.', status: 401 }
    return {
      data: {
        uri: 'otpauth://totp/Actuate%20CMS:admin@example.com?secret=ABCDEF',
        secret: 'ABCDEF234567',
        backupCodes: ['aaaa1111', 'bbbb2222', 'cccc3333', 'dddd4444'],
      },
      status: 200,
    }
  }
  if (endpoint === '/auth/totp/verify' && method === 'POST') {
    totpEnabled = true
    return { data: { enabled: true }, status: 200 }
  }
  if (endpoint === '/auth/totp/disable' && method === 'POST') {
    totpEnabled = false
    return { data: { enabled: false }, status: 200 }
  }
  return { data: {}, status: 200 }
})

vi.mock('../../lib/api.js', () => ({ cmsApi: (e: string, o?: unknown) => cmsApi(e, o as any) }))
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }))
vi.mock('qrcode.react', () => ({
  QRCodeSVG: ({ value }: { value: string }) => <svg data-testid="qr" data-value={value} />,
}))

const { AccountSecurity } = await import('../../views/profile/AccountSecurity.js')

beforeEach(() => {
  cmsApi.mockClear()
  calls.length = 0
  totpEnabled = false
})

describe('AccountSecurity — enrollment', () => {
  it('walks through reauth → QR + backup codes → verify and enables MFA', async () => {
    render(<AccountSecurity />)
    await screen.findByText('Not set up')

    fireEvent.click(screen.getByRole('button', { name: /Set up two-factor/i }))

    // Reauth step: enter current password.
    const pw = await screen.findByLabelText('Current password')
    fireEvent.change(pw, { target: { value: 'correct-horse' } })
    fireEvent.click(screen.getByRole('button', { name: 'Continue' }))

    // Setup posts the password as a reauth header.
    await waitFor(() =>
      expect(
        calls.some(
          (c) =>
            c.endpoint === '/auth/totp/setup' &&
            c.options?.headers?.['x-reauth-password'] === 'correct-horse',
        ),
      ).toBe(true),
    )

    // Enrollment step shows the QR, the manual key and the backup codes.
    await screen.findByTestId('qr')
    expect(screen.getByText('ABCDEF234567')).toBeTruthy()
    expect(screen.getByText('aaaa1111')).toBeTruthy()

    // Verify is gated on acknowledging the backup codes + a 6-digit code.
    const verify = screen.getByRole('button', { name: /Verify & enable/i }) as HTMLButtonElement
    expect(verify.disabled).toBe(true)

    fireEvent.click(screen.getByLabelText(/saved my backup codes/i))
    fireEvent.change(screen.getByLabelText('Authentication code'), { target: { value: '123456' } })
    expect(verify.disabled).toBe(false)
    fireEvent.click(verify)

    await waitFor(() =>
      expect(
        calls.some(
          (c) =>
            c.endpoint === '/auth/totp/verify' && String(c.options?.body ?? '').includes('123456'),
        ),
      ).toBe(true),
    )
    await screen.findByText('Enabled')
  })

  it('surfaces a reauth failure without advancing to the QR step', async () => {
    render(<AccountSecurity />)
    await screen.findByText('Not set up')

    fireEvent.click(screen.getByRole('button', { name: /Set up two-factor/i }))
    const pw = await screen.findByLabelText('Current password')
    fireEvent.change(pw, { target: { value: 'wrong' } })
    fireEvent.click(screen.getByRole('button', { name: 'Continue' }))

    await screen.findByText('Re-authentication failed.')
    expect(screen.queryByTestId('qr')).toBeNull()
  })
})

describe('AccountSecurity — disable', () => {
  it('shows Enabled and disables with a password confirmation', async () => {
    totpEnabled = true
    render(<AccountSecurity />)
    await screen.findByText('Enabled')

    fireEvent.click(screen.getByRole('button', { name: /Disable two-factor/i }))
    const pw = await screen.findByLabelText('Current password')
    fireEvent.change(pw, { target: { value: 'correct-horse' } })
    // The confirm button is the destructive one inside the panel.
    const confirmButtons = screen.getAllByRole('button', { name: /Disable two-factor/i })
    fireEvent.click(confirmButtons[confirmButtons.length - 1]!)

    await waitFor(() => expect(calls.some((c) => c.endpoint === '/auth/totp/disable')).toBe(true))
    await screen.findByText('Not set up')
  })
})
