import { fireEvent, render, screen } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { UndoToastHandle } from './UndoToast'
import { DecisionDialog } from './DecisionDialog'

describe('DecisionDialog', () => {
  beforeEach(() => vi.useFakeTimers())
  afterEach(() => vi.useRealTimers())

  function setup() {
    const onClose = vi.fn()
    const onDecide = vi.fn()
    let undo: (() => void) | null = null
    let timer: ReturnType<typeof setTimeout> | null = null
    const toastHandle: UndoToastHandle = {
      toast: vi.fn(),
      toastUndo: vi.fn((_message, options) => {
        undo = options.onUndo
        timer = setTimeout(options.onCommit, (options.seconds ?? 5) * 1_000)
      }),
    }
    const view = render(
      <DecisionDialog
        detail={{
          taskId: 't4',
          needs: 'Choose whether to mutate production rows',
          why: 'Backfill cannot be reversed automatically',
          blastRadius: 'Affects the orders table',
          options: ['proceed', 'abort'],
        }}
        open
        onClose={onClose}
        onDecide={onDecide}
        toastHandle={toastHandle}
      />,
    )
    return {
      ...view,
      onClose,
      onDecide,
      toastHandle,
      undo: () => undo?.(),
      clearCommit: () => timer && clearTimeout(timer),
    }
  }

  it('holds abort for five seconds and commits exactly once on expiry', () => {
    const { onClose, onDecide, toastHandle } = setup()

    fireEvent.click(screen.getByRole('button', { name: 'abort' }))
    expect(onClose).toHaveBeenCalledTimes(1)
    expect(toastHandle.toastUndo).toHaveBeenCalledWith(
      'Aborting t4 — nothing sent yet.',
      expect.objectContaining({ seconds: 5 }),
    )
    expect(onDecide).not.toHaveBeenCalled()
    vi.advanceTimersByTime(4_999)
    expect(onDecide).not.toHaveBeenCalled()
    vi.advanceTimersByTime(1)
    expect(onDecide).toHaveBeenCalledTimes(1)
    expect(onDecide).toHaveBeenCalledWith('abort')
  })

  it('undoes abort without sending a decision', () => {
    const { onDecide, undo, clearCommit } = setup()

    fireEvent.click(screen.getByRole('button', { name: 'abort' }))
    clearCommit()
    undo()
    vi.advanceTimersByTime(5_000)
    expect(onDecide).not.toHaveBeenCalled()
  })

  it('decides non-abort options immediately', () => {
    const { onDecide } = setup()
    fireEvent.click(screen.getByRole('button', { name: 'proceed' }))
    expect(onDecide).toHaveBeenCalledWith('proceed')
  })

  it('labels detail fields missing from the harness', () => {
    render(
      <DecisionDialog
        detail={{ taskId: 't9', needs: '', why: '', options: [] }}
        open
        onClose={vi.fn()}
        onDecide={vi.fn()}
        toastHandle={{ toast: vi.fn(), toastUndo: vi.fn() }}
      />,
    )

    expect(screen.getAllByText('not provided by harness (A7)')).toHaveLength(4)
  })
})
