import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'

// Mock qrcode before any imports — avoids canvas rendering in happy-dom
vi.mock('qrcode', () => ({
  default: {
    toDataURL: vi.fn().mockResolvedValue('data:image/png;base64,fakeqr'),
  },
}))

// Mock API client
vi.mock('../../../api/client.js', () => ({
  totpSetup: vi.fn().mockResolvedValue({
    provisioning_uri: 'otpauth://totp/DocuVault:test@example.com?secret=JBSWY3DPEHPK3PXP',
    secret: 'JBSWY3DPEHPK3PXP',
  }),
  totpEnable: vi.fn(),
}))

// Mock toast store — capture show() calls so we can assert on them
const mockShow = vi.fn()
vi.mock('../../../stores/toast.js', () => ({
  useToastStore: () => ({ show: mockShow }),
}))

import { totpEnable as totpEnableMock } from '../../../api/client.js'
import TotpEnrollment from '../TotpEnrollment.vue'

beforeEach(() => {
  setActivePinia(createPinia())
  vi.clearAllMocks()
  mockShow.mockClear()
})

describe('TotpEnrollment — QR code rendering (AUTH-03)', () => {
  it('renders an <img> tag with a data:image/ src after startSetup, not an otpauth:// link', async () => {
    const wrapper = mount(TotpEnrollment, {
      global: {
        plugins: [createPinia()],
        stubs: {
          AppSpinner: { template: '<span />' },
          BackupCodesDisplay: { template: '<div />', props: ['codes'] },
        },
      },
    })

    // Initial state — verify step not shown yet
    expect(wrapper.find('img').exists()).toBe(false)

    // Find and click the setup button
    const setupButton = wrapper.find('button')
    expect(setupButton.exists()).toBe(true)
    expect(setupButton.text()).toContain('Set up two-factor authentication')
    await setupButton.trigger('click')

    // Wait for all async operations (totpSetup + QRCode.toDataURL)
    await flushPromises()

    // Assert: <img> must exist with a data:image/ src
    const img = wrapper.find('img')
    expect(img.exists()).toBe(true)
    expect(img.attributes('src')).toMatch(/^data:image\//)

    // Assert: no <a> tag with href starting with otpauth:// (the old link-based approach)
    const links = wrapper.findAll('a')
    const otpauthLinks = links.filter(a =>
      (a.attributes('href') || '').startsWith('otpauth://')
    )
    expect(otpauthLinks).toHaveLength(0)
  })

  it('displays the manual secret key after startSetup', async () => {
    const wrapper = mount(TotpEnrollment, {
      global: {
        plugins: [createPinia()],
        stubs: {
          AppSpinner: { template: '<span />' },
          BackupCodesDisplay: { template: '<div />', props: ['codes'] },
        },
      },
    })

    await wrapper.find('button').trigger('click')
    await flushPromises()

    // The secret JBSWY3DPEHPK3PXP must appear in the rendered output
    expect(wrapper.text()).toContain('JBSWY3DPEHPK3PXP')
  })
})

// ─── GAP 3: Sessions revoked inline alert (CR-02) ───────────────────────────

describe('TotpEnrollment — sessions revoked toast (CR-02)', () => {
  const mountStubs = {
    AppSpinner: { template: '<span />' },
    BackupCodesDisplay: { template: '<div />', props: ['codes'] },
  }

  it('calls toastStore.show("Other sessions have been terminated.") after totpEnable returns sessions_revoked > 0 (CR-02)', async () => {
    vi.mocked(totpEnableMock).mockResolvedValueOnce({
      backup_codes: ['CODE1', 'CODE2'],
      sessions_revoked: 1,
    })

    const wrapper = mount(TotpEnrollment, {
      global: {
        plugins: [createPinia()],
        stubs: mountStubs,
      },
    })

    // Move component from 'setup' step → 'verify' step by clicking the setup button
    await wrapper.find('button').trigger('click')
    await flushPromises()

    // Now in 'verify' step — find the code input and set a 6-digit value
    const codeInput = wrapper.find('input[inputmode="numeric"]')
    expect(codeInput.exists()).toBe(true)
    await codeInput.setValue('123456')

    // Click "Verify code" button to trigger confirmEnrollment()
    const verifyBtn = wrapper.findAll('button').find(b => b.text().includes('Verify code'))
    expect(verifyBtn).toBeDefined()
    await verifyBtn.trigger('click')
    await flushPromises()

    expect(mockShow).toHaveBeenCalledWith('Other sessions have been terminated.', 'success')
  })

  it('does NOT call toastStore.show when totpEnable returns sessions_revoked is 0 (CR-02 negative)', async () => {
    vi.mocked(totpEnableMock).mockResolvedValueOnce({
      backup_codes: [],
      sessions_revoked: 0,
    })

    const wrapper = mount(TotpEnrollment, {
      global: {
        plugins: [createPinia()],
        stubs: mountStubs,
      },
    })

    await wrapper.find('button').trigger('click')
    await flushPromises()

    const codeInput = wrapper.find('input[inputmode="numeric"]')
    await codeInput.setValue('123456')

    const verifyBtn = wrapper.findAll('button').find(b => b.text().includes('Verify code'))
    await verifyBtn.trigger('click')
    await flushPromises()

    expect(mockShow).not.toHaveBeenCalled()
  })
})
