import { render, screen, fireEvent, act } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
import { BreadcrumbBar } from './BreadcrumbBar'
import { formatShortcutDisplay } from '../hooks/appCommandCatalog'
import type { VaultEntry } from '../types'

const dragRegionMouseDown = vi.fn()

vi.mock('../hooks/useDragRegion', () => ({
  useDragRegion: () => ({ onMouseDown: dragRegionMouseDown }),
}))

const baseEntry: VaultEntry = {
  path: '/vault/note/test.md',
  filename: 'test.md',
  title: 'Test Note',
  isA: 'Note',
  aliases: [],
  belongsTo: [],
  relatedTo: [],
  status: null,
  archived: false,
  modifiedAt: 1700000000,
  createdAt: null,
  fileSize: 100,
  snippet: '',
  wordCount: 0,
  relationships: {},
  icon: null,
  color: null,
  order: null,
  outgoingLinks: [],
  template: null,
  sort: null,
  sidebarLabel: null,
  view: null,
  visible: null,
  properties: {},
  organized: false,
  favorite: false,
  favoriteIndex: null,
  listPropertiesDisplay: [],
  hasH1: false,
}

const archivedEntry: VaultEntry = {
  ...baseEntry,
  archived: true,
}

const defaultProps = {
  wordCount: 100,
  showDiffToggle: false,
  diffMode: false,
  diffLoading: false,
  onToggleDiff: vi.fn(),
}

async function expectTooltip(trigger: HTMLElement, ...parts: string[]) {
  act(() => {
    fireEvent.focus(trigger)
  })
  const tooltip = await screen.findByRole('tooltip')
  for (const part of parts) {
    expect(tooltip).toHaveTextContent(part)
  }
  act(() => {
    fireEvent.blur(trigger)
  })
}

describe('BreadcrumbBar — drag region', () => {
  it('forwards mousedown events to the shared drag-region hook', () => {
    const { container } = render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
    const bar = container.querySelector('.breadcrumb-bar') as HTMLElement

    fireEvent.mouseDown(bar, { button: 0 })

    expect(dragRegionMouseDown).toHaveBeenCalledOnce()
  })

  it('has data-tauri-drag-region on the container', () => {
    const { container } = render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
    const bar = container.firstElementChild as HTMLElement
    expect(bar.dataset.tauriDragRegion).toBeDefined()
  })

  it('marks the center spacer as a drag region', () => {
    const { container } = render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
    const spacer = container.querySelector('.breadcrumb-bar__drag-spacer')
    expect(spacer).toHaveAttribute('data-tauri-drag-region')
    expect(spacer).toHaveAttribute('aria-hidden', 'true')
  })
})

describe('BreadcrumbBar — delete', () => {
  it('shows delete button', () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onDelete={vi.fn()} />)
    expect(screen.getByRole('button', { name: 'Delete this note' })).toBeInTheDocument()
  })

  it('calls onDelete when delete button is clicked', () => {
    const onDelete = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onDelete={onDelete} />)
    fireEvent.click(screen.getByRole('button', { name: 'Delete this note' }))
    expect(onDelete).toHaveBeenCalledOnce()
  })
})

describe('BreadcrumbBar — archive/unarchive', () => {
  it('shows archive button for non-archived note', () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onArchive={vi.fn()} onUnarchive={vi.fn()} />)
    expect(screen.getByRole('button', { name: 'Archive this note' })).toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'Restore this archived note' })).not.toBeInTheDocument()
  })

  it('shows unarchive button for archived note', () => {
    render(<BreadcrumbBar entry={archivedEntry} {...defaultProps} onArchive={vi.fn()} onUnarchive={vi.fn()} />)
    expect(screen.getByRole('button', { name: 'Restore this archived note' })).toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'Archive this note' })).not.toBeInTheDocument()
  })

  it('calls onArchive when archive button is clicked', () => {
    const onArchive = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onArchive={onArchive} />)
    fireEvent.click(screen.getByRole('button', { name: 'Archive this note' }))
    expect(onArchive).toHaveBeenCalledOnce()
  })

  it('calls onUnarchive when unarchive button is clicked', () => {
    const onUnarchive = vi.fn()
    render(<BreadcrumbBar entry={archivedEntry} {...defaultProps} onUnarchive={onUnarchive} />)
    fireEvent.click(screen.getByRole('button', { name: 'Restore this archived note' }))
    expect(onUnarchive).toHaveBeenCalledOnce()
  })
})

describe('BreadcrumbBar — file actions', () => {
  it('reveals the current file from the breadcrumb toolbar', () => {
    const onRevealFile = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onRevealFile={onRevealFile} />)

    fireEvent.click(screen.getByRole('button', { name: 'Reveal in Finder' }))

    expect(onRevealFile).toHaveBeenCalledWith('/vault/note/test.md')
  })

  it('copies the current file path from the breadcrumb toolbar', () => {
    const onCopyFilePath = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onCopyFilePath={onCopyFilePath} />)

    fireEvent.click(screen.getByRole('button', { name: 'Copy file path' }))

    expect(onCopyFilePath).toHaveBeenCalledWith('/vault/note/test.md')
  })
})

describe('BreadcrumbBar — organized shortcut hint', () => {
  it('shows Cmd+E on the organized toggle tooltip', async () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onToggleOrganized={vi.fn()} />)
    await expectTooltip(
      screen.getByRole('button', { name: 'Set note as organized' }),
      'Set note as organized',
      formatShortcutDisplay({ display: '⌘E' }),
    )
  })

  it('hides the organized toggle when the workflow is disabled', () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
    expect(screen.queryByRole('button', { name: 'Set note as organized' })).not.toBeInTheDocument()
  })
})

describe('BreadcrumbBar — title in breadcrumb (always rendered, CSS-toggled)', () => {
  it('always renders title elements in the DOM', () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
    expect(screen.getByText('Note')).toBeInTheDocument()
    expect(screen.getByText('›')).toBeInTheDocument()
    expect(screen.getByText('test')).toBeInTheDocument()
  })

  it('renders emoji note icons in the breadcrumb title', () => {
    const entryWithEmoji = { ...baseEntry, icon: '🚀' }
    render(<BreadcrumbBar entry={entryWithEmoji} {...defaultProps} />)
    expect(screen.getByTestId('breadcrumb-note-icon')).toHaveTextContent('🚀')
  })

  it('renders Phosphor note icons in the breadcrumb title', () => {
    const entryWithPhosphor = { ...baseEntry, icon: 'cooking-pot' }
    render(<BreadcrumbBar entry={entryWithPhosphor} {...defaultProps} />)
    expect(screen.getByTestId('breadcrumb-note-icon').tagName.toLowerCase()).toBe('svg')
  })

  it('falls back to "Note" when isA is null', () => {
    const entryNoType = { ...baseEntry, isA: null }
    render(<BreadcrumbBar entry={entryNoType} {...defaultProps} />)
    expect(screen.getByText('Note')).toBeInTheDocument()
  })

  it('separator visibility is controlled by data-title-hidden while using the shared border chrome', () => {
    const { container } = render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
    const bar = container.querySelector('.breadcrumb-bar')!
    expect(bar).toHaveClass('border-b', 'border-transparent')
    expect(bar).toHaveAttribute('data-title-hidden')
  })

  it('keeps the breadcrumb title visible in raw mode', () => {
    const { container } = render(
      <BreadcrumbBar entry={baseEntry} {...defaultProps} rawMode onToggleRaw={vi.fn()} />,
    )

    expect(container.querySelector('.breadcrumb-bar')).toHaveAttribute('data-title-hidden')
  })
})

describe('BreadcrumbBar — filename controls', () => {
  it('shows the sync button when the filename diverges from the title slug', () => {
    const entry = { ...baseEntry, title: 'Fresh Title', filename: 'untitled-note-123.md' }
    render(<BreadcrumbBar entry={entry} {...defaultProps} onRenameFilename={vi.fn()} />)
    expect(screen.getByTestId('breadcrumb-sync-button')).toBeInTheDocument()
  })

  it('hides the sync button when the filename already matches the title slug', () => {
    const entry = { ...baseEntry, title: 'Test Note', filename: 'test-note.md' }
    render(<BreadcrumbBar entry={entry} {...defaultProps} onRenameFilename={vi.fn()} />)
    expect(screen.queryByTestId('breadcrumb-sync-button')).not.toBeInTheDocument()
  })

  it('clicking the sync button renames the file to the title slug', () => {
    const onRenameFilename = vi.fn()
    const entry = { ...baseEntry, title: 'Fresh Title', filename: 'untitled-note-123.md' }
    render(<BreadcrumbBar entry={entry} {...defaultProps} onRenameFilename={onRenameFilename} />)
    fireEvent.click(screen.getByTestId('breadcrumb-sync-button'))
    expect(onRenameFilename).toHaveBeenCalledWith(entry.path, 'fresh-title')
  })

  it('lets keyboard users press Enter on the filename to start editing', () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onRenameFilename={vi.fn()} />)
    fireEvent.keyDown(screen.getByTestId('breadcrumb-filename-trigger'), { key: 'Enter' })
    expect(screen.getByTestId('breadcrumb-filename-input')).toHaveValue('test')
  })

  it('double-clicking the filename enters edit mode and Enter confirms the rename', () => {
    const onRenameFilename = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onRenameFilename={onRenameFilename} />)

    fireEvent.doubleClick(screen.getByTestId('breadcrumb-filename-trigger'))
    const input = screen.getByTestId('breadcrumb-filename-input')
    fireEvent.change(input, { target: { value: 'renamed-file' } })
    fireEvent.keyDown(input, { key: 'Enter' })

    expect(onRenameFilename).toHaveBeenCalledWith(baseEntry.path, 'renamed-file')
  })

  it('pressing Escape while editing cancels the inline rename', () => {
    const onRenameFilename = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onRenameFilename={onRenameFilename} />)

    fireEvent.doubleClick(screen.getByTestId('breadcrumb-filename-trigger'))
    const input = screen.getByTestId('breadcrumb-filename-input')
    fireEvent.change(input, { target: { value: 'renamed-file' } })
    fireEvent.keyDown(input, { key: 'Escape' })

    expect(onRenameFilename).not.toHaveBeenCalled()
    expect(screen.queryByTestId('breadcrumb-filename-input')).not.toBeInTheDocument()
  })

  it('blur confirms the inline rename when the value changed', () => {
    const onRenameFilename = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} onRenameFilename={onRenameFilename} />)

    fireEvent.doubleClick(screen.getByTestId('breadcrumb-filename-trigger'))
    const input = screen.getByTestId('breadcrumb-filename-input')
    fireEvent.change(input, { target: { value: 'renamed-on-blur' } })
    fireEvent.blur(input)

    expect(onRenameFilename).toHaveBeenCalledWith(baseEntry.path, 'renamed-on-blur')
  })
})

describe('BreadcrumbBar — action buttons always right-aligned', () => {
  it('actions container has ml-auto so buttons are always right-aligned', () => {
    const { container } = render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
    const actions = container.querySelector('.breadcrumb-bar__actions')
    expect(actions).toBeInTheDocument()
    expect(actions).toHaveClass('ml-auto')
  })

  it('does not render the unused backlinks or more-actions placeholders', () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
    expect(screen.queryByRole('button', { name: 'Backlinks are coming soon' })).not.toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'More note actions are coming soon' })).not.toBeInTheDocument()
  })
})

describe('BreadcrumbBar — raw editor toggle', () => {
  it('shows Raw editor button with tooltip "Raw editor" when rawMode is off', () => {
    const onToggleRaw = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} rawMode={false} onToggleRaw={onToggleRaw} />)
    expect(screen.getByRole('button', { name: 'Open the raw editor' })).toBeInTheDocument()
  })

  it('shows "Back to editor" tooltip when rawMode is on', () => {
    const onToggleRaw = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} rawMode={true} onToggleRaw={onToggleRaw} />)
    expect(screen.getByRole('button', { name: 'Return to the editor' })).toBeInTheDocument()
  })

  it('calls onToggleRaw when raw button is clicked', () => {
    const onToggleRaw = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} rawMode={false} onToggleRaw={onToggleRaw} />)
    fireEvent.click(screen.getByRole('button', { name: 'Open the raw editor' }))
    expect(onToggleRaw).toHaveBeenCalledOnce()
  })

  it('hides raw toggle when forceRawMode is true (non-markdown file)', () => {
    const onToggleRaw = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} rawMode={true} onToggleRaw={onToggleRaw} forceRawMode={true} />)
    expect(screen.queryByRole('button', { name: 'Open the raw editor' })).not.toBeInTheDocument()
    expect(screen.queryByRole('button', { name: 'Return to the editor' })).not.toBeInTheDocument()
  })

  it('shows raw toggle when forceRawMode is false (markdown file)', () => {
    const onToggleRaw = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} rawMode={false} onToggleRaw={onToggleRaw} forceRawMode={false} />)
    expect(screen.getByRole('button', { name: 'Open the raw editor' })).toBeInTheDocument()
  })
})

describe('BreadcrumbBar — note layout toggle', () => {
  it('shows the left-align layout action while centered', () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} noteLayout="centered" onToggleNoteLayout={vi.fn()} />)

    expect(screen.getByRole('button', { name: 'Switch to left-aligned note layout' })).toBeInTheDocument()
  })

  it('shows the centered layout action while left-aligned', () => {
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} noteLayout="left" onToggleNoteLayout={vi.fn()} />)

    expect(screen.getByRole('button', { name: 'Switch to centered note layout' })).toBeInTheDocument()
  })

  it('calls onToggleNoteLayout when the layout button is clicked', () => {
    const onToggleNoteLayout = vi.fn()
    render(<BreadcrumbBar entry={baseEntry} {...defaultProps} noteLayout="centered" onToggleNoteLayout={onToggleNoteLayout} />)

    fireEvent.click(screen.getByRole('button', { name: 'Switch to left-aligned note layout' }))

    expect(onToggleNoteLayout).toHaveBeenCalledOnce()
  })
})
