import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, act } from '@testing-library/react'
import { useCommitFlow } from './useCommitFlow'

describe('useCommitFlow', () => {
  let savePending: vi.Mock
  let loadModifiedFiles: vi.Mock
  let commitAndPush: vi.Mock
  let setToastMessage: vi.Mock

  beforeEach(() => {
    savePending = vi.fn().mockResolvedValue(undefined)
    loadModifiedFiles = vi.fn().mockResolvedValue(undefined)
    commitAndPush = vi.fn().mockResolvedValue('Committed and pushed')
    setToastMessage = vi.fn()
  })

  function renderCommitFlow() {
    return renderHook(() => useCommitFlow({ savePending, loadModifiedFiles, commitAndPush, setToastMessage }))
  }

  it('openCommitDialog saves pending, refreshes files, then opens dialog', async () => {
    const { result } = renderCommitFlow()
    expect(result.current.showCommitDialog).toBe(false)

    await act(async () => {
      await result.current.openCommitDialog()
    })

    expect(savePending).toHaveBeenCalledTimes(1)
    expect(loadModifiedFiles).toHaveBeenCalledTimes(1)
    expect(result.current.showCommitDialog).toBe(true)
  })

  it('handleCommitPush saves pending, commits, shows toast, and refreshes files', async () => {
    const { result } = renderCommitFlow()

    await act(async () => {
      await result.current.handleCommitPush('test message')
    })

    expect(savePending).toHaveBeenCalled()
    expect(commitAndPush).toHaveBeenCalledWith('test message')
    expect(setToastMessage).toHaveBeenCalledWith('Committed and pushed')
    expect(loadModifiedFiles).toHaveBeenCalled()
    expect(result.current.showCommitDialog).toBe(false)
  })

  it('handleCommitPush shows error toast on failure', async () => {
    commitAndPush.mockRejectedValueOnce(new Error('push failed'))
    const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
    const { result } = renderCommitFlow()

    await act(async () => {
      await result.current.handleCommitPush('test')
    })

    expect(setToastMessage).toHaveBeenCalledWith(expect.stringContaining('Commit failed'))
    consoleSpy.mockRestore()
  })

  it('closeCommitDialog closes the dialog', async () => {
    const { result } = renderCommitFlow()

    await act(async () => {
      await result.current.openCommitDialog()
    })
    expect(result.current.showCommitDialog).toBe(true)

    act(() => {
      result.current.closeCommitDialog()
    })
    expect(result.current.showCommitDialog).toBe(false)
  })
})
