// @vitest-environment jsdom
import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'
import React from 'react'

vi.mock('../src/renderer/src/terminals', () => {
  let n = 0
  return { Terminal: () => null, lid: () => ++n }
})

import { App } from '../src/renderer/src/App'
import { ProjectProvider } from '../src/renderer/src/project'

beforeAll(() => {
  globalThis.ResizeObserver = class { observe() {} unobserve() {} disconnect() {} } as never
  if (!Element.prototype.scrollIntoView) Element.prototype.scrollIntoView = () => {}
})
afterEach(() => { cleanup(); localStorage.clear() })

function renderApp(): HTMLElement {
  return render(<ProjectProvider><App /></ProjectProvider>).container
}
function find(c: HTMLElement, sel: string, text: string): HTMLElement | undefined {
  return Array.from(c.querySelectorAll<HTMLElement>(sel)).find((el) => el.textContent?.includes(text))
}
// The tree opens fully collapsed, so expand each ancestor folder before reaching
// a nested file. Each folder is clicked exactly once (a second click collapses).
async function expandTo(c: HTMLElement, ...folders: string[]): Promise<void> {
  for (const name of folders) {
    const row = await waitFor(() => {
      const r = find(c, '.tree-row', name)
      if (!r) throw new Error(`folder ${name} not ready`)
      return r
    })
    fireEvent.click(row)
  }
}

describe('Pass on to Agent', () => {
  it('inserts "<note> => <path:line>" via the agentPaste event', async () => {
    const received: string[] = []
    const handler = (e: Event): void => { received.push((e as CustomEvent<string>).detail) }
    window.addEventListener('agentPaste', handler)
    try {
      const c = renderApp()
      await expandTo(c, 'public', 'assets')
      const treeRow = await waitFor(() => {
        const r = find(c, '.tree-row', 'store.js')
        if (!r) throw new Error('tree not ready')
        return r
      })
      fireEvent.click(treeRow)
      const ta = await waitFor(() => {
        const t = c.querySelector<HTMLTextAreaElement>('.ce-ta')
        if (!t) throw new Error('editor not ready')
        return t
      })
      fireEvent.contextMenu(ta)
      const pass = await waitFor(() => {
        const item = find(c, '.ctx-item', 'Pass on reference')
        if (!item) throw new Error('menu not open')
        return item
      })
      fireEvent.click(pass)
      const input = await waitFor(() => {
        const i = c.querySelector<HTMLInputElement>('.pass-input')
        if (!i) throw new Error('popup not open')
        return i
      })
      fireEvent.change(input, { target: { value: 'look here' } })
      fireEvent.keyDown(input, { key: 'Enter' })
      await waitFor(() => expect(received.length).toBeGreaterThan(0))
      expect(received[0]).toBe('look here => public/assets/store.js:1')
    } finally {
      window.removeEventListener('agentPaste', handler)
    }
  })
})

describe('Stage + commit', () => {
  it('stages a file and commits via Shift+Enter (no commit button)', async () => {
    const c = renderApp()
    const row = await waitFor(() => {
      const r = find(c, '.git-row', 'UserController.php')
      if (!r) throw new Error('git not ready')
      return r
    })
    fireEvent.click(row.querySelector<HTMLButtonElement>('button[title="Stage changes"]')!)
    // the commit button is intentionally gone — committing is keyboard-only
    expect(c.querySelector('.commit-btn')).toBeNull()
    const msg = c.querySelector<HTMLTextAreaElement>('.commit-input')!
    fireEvent.change(msg, { target: { value: 'wire up balance' } })
    fireEvent.keyDown(msg, { key: 'Enter', shiftKey: true })
    await waitFor(() => expect(find(c, '.toast', 'Committed')).toBeTruthy())
  })
})
