import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount, flushPromises } from '@vue/test-utils'
import { setActivePinia, createPinia } from 'pinia'
import AuditTrailPanel from '@/components/AuditTrailPanel.vue'
import { useBatchStore } from '@/stores/batches'
import type { BatchEventResponse } from '@/types'

vi.mock('@/api/client', () => ({
  get: vi.fn(),
  post: vi.fn(),
  put: vi.fn(),
  del: vi.fn(),
  patch: vi.fn(),
  uploadFile: vi.fn(),
}))

function makeEvent(overrides: Partial<BatchEventResponse> = {}): BatchEventResponse {
  return {
    id: 'evt-1',
    batchId: 'b1',
    eventType: 'STATUS_CHANGED',
    actorUserId: 'u1',
    actorUsername: 'entry1',
    actorFullName: 'Entry Clerk 1',
    occurredAt: '2026-06-27T10:00:00Z',
    metadataJson: JSON.stringify({ previousStatus: 'DRAFT', newStatus: 'PENDING_VERIFICATION' }),
    ...overrides,
  }
}

describe('AuditTrailPanel', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
  })

  it('shows empty state when opened with no events', async () => {
    const store = useBatchStore()
    store.events = []
    store.eventsLoading = false
    store.eventsError = null

    vi.spyOn(store, 'fetchEvents').mockResolvedValue()

    const wrapper = mount(AuditTrailPanel, { props: { batchId: 'b1' } })
    const details = wrapper.find('details')
    details.element.open = true
    await details.trigger('toggle')
    await flushPromises()

    expect(wrapper.text()).toContain('No audit events for this batch yet.')
  })

  it('renders events as a vertical timeline', async () => {
    const store = useBatchStore()
    store.events = [
      makeEvent(),
      makeEvent({
        id: 'evt-2',
        eventType: 'VERIFIED',
        actorFullName: 'Verifier One',
        metadataJson: null,
      }),
    ]
    store.eventsLoading = false
    store.eventsHasMore = false

    vi.spyOn(store, 'fetchEvents').mockResolvedValue()

    const wrapper = mount(AuditTrailPanel, { props: { batchId: 'b1' } })
    const details = wrapper.find('details')
    details.element.open = true
    await details.trigger('toggle')
    await flushPromises()

    expect(wrapper.find('[data-testid="audit-trail-timeline"]').exists()).toBe(true)
    expect(wrapper.find('table').exists()).toBe(false)
    expect(wrapper.text()).toContain('Status Changed')
    expect(wrapper.text()).toContain('Entry Clerk 1')
    expect(wrapper.text()).toContain('DRAFT → PENDING_VERIFICATION')
    expect(wrapper.text()).toContain('Verified')
    expect(wrapper.text()).toContain('Verifier One')
  })

  it('shows load more when hasMore is true', async () => {
    const store = useBatchStore()
    store.events = [makeEvent()]
    store.eventsHasMore = true
    store.eventsNextCursor = 'cursor-1'

    const fetchSpy = vi.spyOn(store, 'fetchEvents').mockResolvedValue()

    const wrapper = mount(AuditTrailPanel, { props: { batchId: 'b1' } })
    const details = wrapper.find('details')
    details.element.open = true
    await details.trigger('toggle')
    await flushPromises()

    const loadMore = wrapper.find('[data-testid="audit-trail-load-more"]')
    expect(loadMore.exists()).toBe(true)
    await loadMore.trigger('click')
    expect(fetchSpy).toHaveBeenCalledWith('b1', 'cursor-1')
  })
})
