import { fireEvent, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
import { APP_COMMAND_IDS, getAppCommandShortcutDisplay } from '../hooks/appCommandCatalog'
import { makeEntry, mockEntries, renderNoteList } from '../test-utils/noteListTestUtils'

function setViewportSize(width: number, height: number) {
  Object.defineProperty(window, 'innerWidth', { value: width, configurable: true })
  Object.defineProperty(window, 'innerHeight', { value: height, configurable: true })
}

function renderNoteListWithFullActionMenu() {
  renderNoteList({
    canCopyGitUrl: vi.fn(() => true),
    onBulkArchive: vi.fn(),
    onBulkDeletePermanently: vi.fn(),
    onCopyFilePath: vi.fn(),
    onCopyGitUrl: vi.fn(),
    onEnterNeighborhood: vi.fn(),
    onExportPdf: vi.fn(),
    onOpenInNewWindow: vi.fn(),
    onRenameFilename: vi.fn(),
    onRevealFile: vi.fn(),
    onToggleFavorite: vi.fn(),
    onToggleOrganized: vi.fn(),
  })
}

function openBuildLaputaActions() {
  fireEvent.contextMenu(screen.getByText('Build Laputa App'))
}

function clickBuildLaputaAction(label: string) {
  openBuildLaputaActions()
  fireEvent.click(screen.getByText(label))
}

describe('NoteList context menu', () => {
  it('opens note actions from a right-clicked note item', () => {
    const onOpenInNewWindow = vi.fn()
    const onEnterNeighborhood = vi.fn()
    const onBulkArchive = vi.fn()
    const onBulkDeletePermanently = vi.fn()
    const onExportPdf = vi.fn()
    const onToggleFavorite = vi.fn()
    const onToggleOrganized = vi.fn()
    const onRenameFilename = vi.fn()
    const onRevealFile = vi.fn()
    const onCopyFilePath = vi.fn()
    const canCopyGitUrl = vi.fn(() => true)
    const onCopyGitUrl = vi.fn()

    renderNoteList({
      onOpenInNewWindow,
      onEnterNeighborhood,
      onBulkArchive,
      onBulkDeletePermanently,
      onExportPdf,
      onToggleFavorite,
      onToggleOrganized,
      onRenameFilename,
      onRevealFile,
      onCopyFilePath,
      canCopyGitUrl,
      onCopyGitUrl,
    })

    openBuildLaputaActions()

    expect(screen.getByTestId('note-list-context-menu')).toBeInTheDocument()
    expect(screen.getByTestId('note-list-context-menu')).toHaveClass('z-[12000]')
    expect(screen.getByText(getAppCommandShortcutDisplay(APP_COMMAND_IDS.noteOpenInNewWindow)!)).toBeInTheDocument()
    expect(screen.getByText(getAppCommandShortcutDisplay(APP_COMMAND_IDS.noteToggleFavorite)!)).toBeInTheDocument()
    expect(screen.getByText(getAppCommandShortcutDisplay(APP_COMMAND_IDS.noteToggleOrganized)!)).toBeInTheDocument()
    expect(screen.getByText(getAppCommandShortcutDisplay(APP_COMMAND_IDS.noteDelete)!)).toBeInTheDocument()

    fireEvent.click(screen.getByText('Open in New Window'))
    expect(onOpenInNewWindow).toHaveBeenCalledWith(mockEntries[0])

    clickBuildLaputaAction('Add to Favorites')
    expect(onToggleFavorite).toHaveBeenCalledWith(mockEntries[0].path)

    clickBuildLaputaAction('Mark as Organized')
    expect(onToggleOrganized).toHaveBeenCalledWith(mockEntries[0].path)

    clickBuildLaputaAction('Rename filename')
    expect(screen.getByTestId('note-list-rename-dialog')).toBeInTheDocument()
    expect(screen.getByTestId('note-list-rename-input')).toHaveValue('26q1-laputa-app')
    fireEvent.change(screen.getByTestId('note-list-rename-input'), { target: { value: 'renamed-from-menu.md ' } })
    fireEvent.click(screen.getByText('Rename'))
    expect(onRenameFilename).toHaveBeenCalledWith(mockEntries[0].path, 'renamed-from-menu')

    clickBuildLaputaAction("Open note's neighborhood")
    expect(onEnterNeighborhood).toHaveBeenCalledWith(mockEntries[0])

    clickBuildLaputaAction('Reveal in Finder')
    expect(onRevealFile).toHaveBeenCalledWith(mockEntries[0].path)

    clickBuildLaputaAction('Copy file path')
    expect(onCopyFilePath).toHaveBeenCalledWith(mockEntries[0].path)

    clickBuildLaputaAction('Copy git URL')
    expect(canCopyGitUrl).toHaveBeenCalledWith(mockEntries[0])
    expect(onCopyGitUrl).toHaveBeenCalledWith(mockEntries[0])

    clickBuildLaputaAction('Export note as PDF')
    expect(onExportPdf).toHaveBeenCalledWith(mockEntries[0])

    clickBuildLaputaAction('Archive this note')
    expect(onBulkArchive).toHaveBeenCalledWith([mockEntries[0].path])

    clickBuildLaputaAction('Delete this note')
    expect(onBulkDeletePermanently).toHaveBeenCalledWith([mockEntries[0].path])
  })

  it('shows stateful favorite and organized labels for pinned notes', () => {
    renderNoteList({
      entries: [
        makeEntry({
          favorite: true,
          organized: true,
          path: '/vault/stateful.md',
          title: 'Stateful Note',
        }),
      ],
      onToggleFavorite: vi.fn(),
      onToggleOrganized: vi.fn(),
    })

    fireEvent.contextMenu(screen.getByText('Stateful Note'))

    expect(screen.getByText('Remove from Favorites')).toBeInTheDocument()
    expect(screen.getByText('Mark as Unorganized')).toBeInTheDocument()
  })

  it('hides the organized action for PDF file rows while keeping file actions', () => {
    const pdfEntry = makeEntry({
      fileKind: 'binary',
      filename: 'research.pdf',
      path: '/vault/research.pdf',
      title: 'research.pdf',
    })
    const onCopyFilePath = vi.fn()
    const onRevealFile = vi.fn()
    const onRenameFilename = vi.fn()
    const onToggleOrganized = vi.fn()

    renderNoteList({
      allNotesFileVisibility: { pdfs: true, images: false, unsupported: false },
      entries: [pdfEntry],
      onCopyFilePath,
      onRenameFilename,
      onRevealFile,
      onToggleOrganized,
    })

    fireEvent.contextMenu(screen.getByTestId('pdf-file-item'))

    expect(screen.getByTestId('note-list-context-menu')).toBeInTheDocument()
    expect(screen.queryByText('Mark as Organized')).not.toBeInTheDocument()
    expect(screen.queryByText('Rename filename')).not.toBeInTheDocument()
    expect(screen.getByText('Reveal in Finder')).toBeInTheDocument()

    fireEvent.click(screen.getByText('Copy file path'))
    expect(onCopyFilePath).toHaveBeenCalledWith(pdfEntry.path)
    expect(onToggleOrganized).not.toHaveBeenCalled()
  })

  it('hides the git URL action for notes without a remote', () => {
    renderNoteList({
      canCopyGitUrl: () => false,
      onCopyGitUrl: vi.fn(),
      onCopyFilePath: vi.fn(),
    })

    fireEvent.contextMenu(screen.getByText('Build Laputa App'))

    expect(screen.queryByText('Copy git URL')).not.toBeInTheDocument()
  })

  it('keeps note actions visible when opened near the bottom-right viewport edge', () => {
    setViewportSize(1024, 768)
    renderNoteList({
      onOpenInNewWindow: vi.fn(),
      onBulkDeletePermanently: vi.fn(),
    })

    fireEvent.contextMenu(screen.getByText('Build Laputa App'), { clientX: 1000, clientY: 740 })

    const menu = screen.getByTestId('note-list-context-menu')
    expect(menu.style.left).toBe('')
    expect(menu.style.top).toBe('')
    expect(menu).toHaveStyle({
      bottom: '28px',
      maxHeight: '732px',
      right: '24px',
    })
  })

  it('caps note actions to the available viewport space from a mid-height right-click', () => {
    setViewportSize(420, 320)
    renderNoteListWithFullActionMenu()

    fireEvent.contextMenu(screen.getByText('Build Laputa App'), { clientX: 271, clientY: 157 })

    const menu = screen.getByTestId('note-list-context-menu')
    expect(menu).toHaveStyle({
      maxHeight: '155px',
      overflowY: 'auto',
      right: '149px',
      top: '157px',
    })
  })
})
