import { describe, it, expect, vi } from 'vitest'
import type { VaultEntry } from '../types'
import { buildRawEditorAutocompleteState, buildRawEditorBaseItems, detectYamlError, extractWikilinkQuery, getRawEditorDropdownPosition, replaceActiveWikilinkQuery } from './rawEditorUtils'

describe('extractWikilinkQuery', () => {
  it('returns null when no [[ trigger', () => {
    expect(extractWikilinkQuery('hello world', 5)).toBeNull()
  })

  it('returns empty string immediately after [[', () => {
    const text = 'see [['
    expect(extractWikilinkQuery(text, text.length)).toBe('')
  })

  it('returns query after [[', () => {
    const text = 'see [[Proj'
    expect(extractWikilinkQuery(text, text.length)).toBe('Proj')
  })

  it('returns null when ]] closes the link', () => {
    const text = '[[Proj]]'
    expect(extractWikilinkQuery(text, text.length)).toBeNull()
  })

  it('returns null when newline is in query', () => {
    const text = '[[Proj\ncontinued'
    expect(extractWikilinkQuery(text, text.length)).toBeNull()
  })

  it('handles cursor before end of text', () => {
    const text = '[[Proj after'
    expect(extractWikilinkQuery(text, 6)).toBe('Proj')
  })
})

describe('replaceActiveWikilinkQuery', () => {
  it('replaces the active wikilink query with the canonical target', () => {
    expect(replaceActiveWikilinkQuery('See [[Proj', 10, 'projects/alpha')).toEqual({
      text: 'See [[projects/alpha]]',
      cursor: 22,
    })
  })

  it('preserves text after the cursor', () => {
    expect(replaceActiveWikilinkQuery('See [[Proj today', 10, 'projects/alpha')).toEqual({
      text: 'See [[projects/alpha]] today',
      cursor: 22,
    })
  })

  it('returns null when no active wikilink trigger exists', () => {
    expect(replaceActiveWikilinkQuery('See Proj', 8, 'projects/alpha')).toBeNull()
  })
})

describe('detectYamlError', () => {
  it('returns null for content without frontmatter', () => {
    expect(detectYamlError('# Title\n\nSome content.')).toBeNull()
  })

  it('returns null for valid frontmatter', () => {
    expect(detectYamlError('---\ntitle: My Note\n---\n\n# Title')).toBeNull()
  })

  it('returns null for valid CRLF frontmatter', () => {
    expect(detectYamlError('---\r\ntitle: My Note\r\n---\r\n\r\n# Title')).toBeNull()
  })

  it('returns error for unclosed frontmatter', () => {
    const error = detectYamlError('---\ntitle: My Note\n\n# Title')
    expect(error).toContain('Unclosed frontmatter')
  })

  it('returns error for tab indentation in frontmatter', () => {
    const error = detectYamlError('---\n\ttitle: My Note\n---\n')
    expect(error).toContain('tab indentation')
  })

  it('returns null for content not starting with ---', () => {
    expect(detectYamlError('Not frontmatter')).toBeNull()
  })
})

describe('buildRawEditorBaseItems', () => {
  it('includes filename aliases and deduplicates entries by path', () => {
    const entry = {
      title: 'Project Alpha',
      aliases: ['Alpha'],
      filename: 'project-alpha.md',
      isA: 'Project',
      path: 'projects/project-alpha.md',
    } as VaultEntry
    expect(buildRawEditorBaseItems([
      entry,
      {
        title: 'Project Alpha',
        aliases: ['Ignored'],
        filename: 'project-alpha.md',
        isA: 'Project',
        path: 'projects/project-alpha.md',
      },
    ] as VaultEntry[])).toEqual([
      {
        title: 'Project Alpha',
        aliases: ['project-alpha', 'Alpha'],
        group: 'Project',
        entry,
        entryType: 'Project',
        entryTitle: 'Project Alpha',
        path: 'projects/project-alpha.md',
      },
    ])
  })
})

describe('buildRawEditorAutocompleteState', () => {
  it('uses workspace metadata for display and prefixes inserted cross-workspace links', () => {
    const personalWorkspace = {
      id: 'personal',
      label: 'Personal',
      alias: 'personal',
      path: '/personal',
      shortLabel: 'PE',
      color: 'blue',
      icon: null,
      mounted: true,
      available: true,
      defaultForNewNotes: true,
    }
    const teamWorkspace = {
      id: 'team',
      label: 'Team',
      alias: 'team',
      path: '/team',
      shortLabel: 'TE',
      color: 'green',
      icon: null,
      mounted: true,
      available: true,
      defaultForNewNotes: false,
    }
    const source = {
      path: '/personal/source.md',
      filename: 'source.md',
      title: 'Source',
      aliases: [],
      isA: null,
      workspace: personalWorkspace,
    } as VaultEntry
    const target = {
      path: '/team/projects/alpha.md',
      filename: 'alpha.md',
      title: 'Alpha',
      aliases: [],
      isA: null,
      workspace: teamWorkspace,
    } as VaultEntry
    const insertTarget = vi.fn()
    const view = {
      state: { selection: { main: { head: 0 } } },
      coordsAtPos: () => ({ bottom: 20, left: 30 }),
    } as never

    const result = buildRawEditorAutocompleteState({
      view,
      baseItems: buildRawEditorBaseItems([source, target]),
      query: 'Alpha',
      typeEntryMap: {},
      onInsertTarget: insertTarget,
      sourceEntry: source,
      vaultPath: '/personal',
    })

    expect(result?.items[0].workspace).toBe(teamWorkspace)
    result?.items[0].onItemClick()
    expect(insertTarget).toHaveBeenCalledWith('team/projects/alpha')
  })
})

describe('getRawEditorDropdownPosition', () => {
  it('renders above the cursor when there is not enough space below', () => {
    expect(getRawEditorDropdownPosition(
      { caretTop: 500, caretLeft: 900, selectedIndex: 0, items: [] },
      200,
      { innerHeight: 640, innerWidth: 1000 },
    )).toEqual({ top: 276, left: 740 })
  })
})
