import { describe, expect, it, vi } from 'vitest'
import {
  detectFileOperation,
  parseBashFileCreation,
  type AgentFileCallbacks,
} from './aiAgentFileOperations'

const VAULT = '/Users/luca/Laputa'

function makeCallbacks() {
  return {
    onFileCreated: vi.fn(),
    onFileModified: vi.fn(),
    onVaultChanged: vi.fn(),
  } satisfies AgentFileCallbacks
}

describe('detectFileOperation', () => {
  it('calls onFileCreated for Write tool with .md in vault', () => {
    const cb = makeCallbacks()
    detectFileOperation({ toolName: 'Write', input: JSON.stringify({ file_path: `${VAULT}/note/test.md` }), vaultPath: VAULT, callbacks: cb })
    expect(cb.onFileCreated).toHaveBeenCalledWith('note/test.md')
    expect(cb.onFileModified).not.toHaveBeenCalled()
  })

  it('calls onFileCreated for create_note tool with relative markdown path', () => {
    const cb = makeCallbacks()
    detectFileOperation({ toolName: 'create_note', input: JSON.stringify({ path: 'note/generated.md' }), vaultPath: VAULT, callbacks: cb })
    expect(cb.onFileCreated).toHaveBeenCalledWith('note/generated.md')
    expect(cb.onVaultChanged).not.toHaveBeenCalled()
  })

  it('calls onFileCreated for create_note tool with Windows absolute path', () => {
    const cb = makeCallbacks()
    detectFileOperation({
      toolName: 'create_note',
      input: JSON.stringify({ path: String.raw`D:\Notes\Notas\nota-longa-teste-gerada-2.md` }),
      vaultPath: String.raw`D:\Notes\Notas`,
      callbacks: cb,
    })
    expect(cb.onFileCreated).toHaveBeenCalledWith('nota-longa-teste-gerada-2.md')
    expect(cb.onVaultChanged).not.toHaveBeenCalled()
  })

  it('calls onFileModified for Edit tool with .md in vault', () => {
    const cb = makeCallbacks()
    detectFileOperation({ toolName: 'Edit', input: JSON.stringify({ file_path: `${VAULT}/note/test.md` }), vaultPath: VAULT, callbacks: cb })
    expect(cb.onFileModified).toHaveBeenCalledWith('note/test.md')
    expect(cb.onFileCreated).not.toHaveBeenCalled()
  })

  it('refreshes the vault when a writable tool target cannot be resolved', () => {
    const cb = makeCallbacks()
    detectFileOperation({ toolName: 'Write', input: 'not-json', vaultPath: VAULT, callbacks: cb })
    detectFileOperation({ toolName: 'Edit', vaultPath: VAULT, callbacks: cb })
    expect(cb.onVaultChanged).toHaveBeenCalledTimes(2)
  })

  it('does not treat path prefixes as files inside the vault', () => {
    const cb = makeCallbacks()
    detectFileOperation({ toolName: 'Write', input: JSON.stringify({ file_path: `${VAULT}-old/note.md` }), vaultPath: VAULT, callbacks: cb })
    expect(cb.onFileCreated).not.toHaveBeenCalled()
    expect(cb.onVaultChanged).toHaveBeenCalledOnce()
  })

  it('detects Bash redirects and tee writes for markdown files in the vault', () => {
    expect(parseBashFileCreation({ input: JSON.stringify({ command: `echo "# Title" > ${VAULT}/note.md` }), vaultPath: VAULT })).toBe('note.md')
    expect(parseBashFileCreation({ input: JSON.stringify({ command: `echo "line" >> ${VAULT}/sub/note.md` }), vaultPath: VAULT })).toBe('sub/note.md')
    expect(parseBashFileCreation({ input: JSON.stringify({ command: `echo "data" | tee -a ${VAULT}/new.md` }), vaultPath: VAULT })).toBe('new.md')
  })

  it('refreshes the vault for Bash when no specific markdown target is found', () => {
    const cb = makeCallbacks()
    detectFileOperation({ toolName: 'Bash', input: JSON.stringify({ command: 'ls -la' }), vaultPath: VAULT, callbacks: cb })
    expect(cb.onFileCreated).not.toHaveBeenCalled()
    expect(cb.onVaultChanged).toHaveBeenCalledOnce()
  })

  it('ignores read-only tools and missing callbacks', () => {
    const cb = makeCallbacks()
    expect(() => detectFileOperation({ toolName: 'Write', input: JSON.stringify({ file_path: `${VAULT}/note/test.md` }), vaultPath: VAULT })).not.toThrow()
    detectFileOperation({ toolName: 'Read', input: JSON.stringify({ file_path: `${VAULT}/note/test.md` }), vaultPath: VAULT, callbacks: cb })
    expect(cb.onFileCreated).not.toHaveBeenCalled()
    expect(cb.onVaultChanged).not.toHaveBeenCalled()
  })
})
