import { AxiosError } from 'axios'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const storage = new Map<string, string>()
const routeQuery = { folder_id: undefined as string | undefined }
const routerReplace = vi.fn(async ({ query }: { query: Record<string, unknown> }) => {
  routeQuery.folder_id = typeof query.folder_id === 'string' ? query.folder_id : undefined
})

const mocks = vi.hoisted(() => ({
  folderGetTree: vi.fn(),
  folderCreate: vi.fn(),
  documentList: vi.fn(),
  documentUpload: vi.fn(),
  documentGetContent: vi.fn(),
  documentGetChunks: vi.fn(),
  documentUpdateChunk: vi.fn(),
}))

vi.mock('vue-router', () => ({
  useRoute: () => ({ query: routeQuery }),
  useRouter: () => ({ replace: routerReplace }),
}))

vi.mock('@/api/folder', () => ({
  folderApi: {
    getTree: mocks.folderGetTree,
    create: mocks.folderCreate,
  },
}))

vi.mock('@/api/document', () => ({
  documentApi: {
    list: mocks.documentList,
    upload: mocks.documentUpload,
    getContent: mocks.documentGetContent,
    getChunks: mocks.documentGetChunks,
    updateChunk: mocks.documentUpdateChunk,
  },
}))

import { useKnowledgeView } from './useKnowledgeView'

describe('useKnowledgeView chunk editing', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    routeQuery.folder_id = undefined
    routerReplace.mockClear()
    storage.clear()
    vi.stubGlobal('localStorage', {
      getItem: vi.fn((key: string) => storage.get(key) ?? null),
      setItem: vi.fn((key: string, value: string) => {
        storage.set(key, value)
      }),
      removeItem: vi.fn((key: string) => {
        storage.delete(key)
      }),
    })
    mocks.folderGetTree.mockResolvedValue({ data: [] })
    mocks.documentList.mockResolvedValue({ data: [] })
    mocks.documentUpload.mockResolvedValue({
      data: {
        id: 'doc-2',
        title: 'New doc',
        chunk_count: 1,
        status: '上传成功，正在索引...',
      },
    })
    mocks.documentGetContent.mockResolvedValue({ data: { content: 'document preview' } })
    mocks.documentGetChunks.mockResolvedValue({
      data: [
        {
          id: 'chunk-1',
          chunk_index: 0,
          content: 'original chunk content',
          metadata_: '{"content_type":"paragraph","section_title":"Intro"}',
        },
      ],
    })
    mocks.documentUpdateChunk.mockResolvedValue({
      data: {
        id: 'chunk-1',
        chunk_index: 0,
        content: 'edited chunk content',
        metadata_: '{"content_type":"paragraph","section_title":"Intro"}',
      },
    })
  })

  it('loads chunks when opening a document and updates local chunk state after save', async () => {
    const view = useKnowledgeView()
    const document = {
      id: 'doc-1',
      title: 'Test doc',
      filename: 'test.md',
      file_type: 'md',
      file_size: 128,
      chunk_count: 1,
      is_indexed: true,
      created_at: '2026-03-22T00:00:00Z',
    }

    await view.openDocument(document)

    expect(mocks.documentGetChunks).toHaveBeenCalledWith('doc-1')
    expect(view.activeDocumentChunks.value).toHaveLength(1)
    expect(view.activeDocumentChunks.value[0].content).toBe('original chunk content')

    view.startChunkEdit(view.activeDocumentChunks.value[0])
    view.chunkDrafts.value['chunk-1'] = 'edited chunk content'
    await view.saveChunkEdit('chunk-1')

    expect(mocks.documentUpdateChunk).toHaveBeenCalledWith('doc-1', 'chunk-1', { content: 'edited chunk content' })
    expect(view.activeDocumentChunks.value[0].content).toBe('edited chunk content')
    expect(view.chunkEditError.value['chunk-1']).toBe('')
  })

  it('shows a newly uploaded document immediately in the current folder list', async () => {
    const view = useKnowledgeView()
    const folder = {
      id: 'folder-1',
      name: '测试',
      parent_id: null,
      children: [],
      created_at: '2026-03-22T00:00:00Z',
    }
    mocks.folderGetTree.mockResolvedValue({ data: [folder] })
    mocks.documentList
      .mockResolvedValueOnce({ data: [] })
      .mockResolvedValueOnce({ data: [] })
    const file = new File(['hello'], 'new-doc.md', { type: 'text/markdown' })
    const event = { target: { files: [file], value: 'new-doc.md' } } as unknown as Event

    await view.enterFolder(folder)
    await view.handleUpload(event)

    expect(view.documents.value).toHaveLength(1)
    expect(view.documents.value[0].id).toBe('doc-2')
    expect(view.documents.value[0].title).toBe('New doc')
    expect(view.documents.value[0].filename).toBe('new-doc.md')
    expect(view.documents.value[0].folder_id).toBe('folder-1')
    expect(routeQuery.folder_id).toBe('folder-1')
    expect(view.highlightedDocumentId.value).toBe('doc-2')
    expect(view.uploadSuccess.value).toBe('已上传到 测试')
  })

  it('restores the selected folder and reloads its documents after refresh', async () => {
    routeQuery.folder_id = 'folder-1'
    storage.set('knowledge.currentFolderId', 'folder-1')
    const folder = {
      id: 'folder-1',
      name: '测试',
      parent_id: null,
      children: [],
      created_at: '2026-03-22T00:00:00Z',
    }
    const view = useKnowledgeView()

    mocks.folderGetTree.mockResolvedValue({ data: [folder] })
    mocks.documentList.mockResolvedValue({
      data: [
        {
          id: 'doc-3',
          title: 'Persisted doc',
          filename: 'persisted.md',
          file_type: 'md',
          file_size: 512,
          chunk_count: 2,
          is_indexed: true,
          folder_id: 'folder-1',
          created_at: '2026-03-22T00:00:00Z',
        },
      ],
    })

    await view.goToFolder('folder-1')

    expect(view.currentFolderId.value).toBe('folder-1')
    expect(mocks.documentList).toHaveBeenCalledWith('folder-1')
    expect(view.documents.value).toHaveLength(1)
    expect(view.documents.value[0].id).toBe('doc-3')
  })

  it('stays on the current listing after creating a folder and shows the new folder', async () => {
    const createdFolder = {
      id: 'folder-new',
      name: '新文件夹',
      parent_id: null,
      children: [],
      created_at: '2026-03-22T00:00:00Z',
      updated_at: '2026-03-22T00:00:00Z',
    }

    mocks.folderCreate.mockResolvedValue({ data: createdFolder })
    mocks.folderGetTree.mockResolvedValue({
      data: [
        {
          id: 'folder-new',
          name: '新文件夹',
          parent_id: null,
          children: [],
        },
      ],
    })

    const view = useKnowledgeView()
    view.showNewFolderDialog.value = true
    view.newFolderName.value = '新文件夹'
    await view.createFolder()

    expect(view.currentFolderId.value).toBe(null)
    expect(routeQuery.folder_id).toBeUndefined()
    expect(storage.get('knowledge.currentFolderId')).toBeUndefined()
    expect(view.showNewFolderDialog.value).toBe(false)
    expect(view.visibleFolders.value).toHaveLength(1)
    expect(view.visibleFolders.value[0].id).toBe('folder-new')
  })

  it('loads documents at the root view instead of clearing the list', async () => {
    const view = useKnowledgeView()

    mocks.documentList.mockResolvedValue({
      data: [
        {
          id: 'doc-root',
          title: 'Root doc',
          filename: 'root.md',
          file_type: 'md',
          file_size: 128,
          chunk_count: 1,
          is_indexed: true,
          folder_id: null,
          created_at: '2026-03-22T00:00:00Z',
        },
      ],
    })

    await view.goToFolder(null)

    expect(mocks.documentList).toHaveBeenCalledWith(null)
    expect(view.documents.value).toHaveLength(1)
    expect(view.documents.value[0].id).toBe('doc-root')
  })

  it('resets a stale persisted folder before creating a new folder', async () => {
    routeQuery.folder_id = 'stale-folder'
    storage.set('knowledge.currentFolderId', 'stale-folder')
    mocks.folderGetTree.mockResolvedValue({ data: [] })
    mocks.folderCreate.mockResolvedValue({
      data: {
        id: 'folder-fresh',
        name: '全新文件夹',
        parent_id: null,
        created_at: '2026-03-22T00:00:00Z',
        updated_at: '2026-03-22T00:00:00Z',
      },
    })

    const view = useKnowledgeView()
    await view.goToFolder(null)
    view.newFolderName.value = '全新文件夹'
    await view.createFolder()

    expect(mocks.folderCreate).toHaveBeenCalledWith({
      name: '全新文件夹',
      parent_id: null,
    })
  })

  it('shows a clear upload message when mineru dependency is missing', async () => {
    const folder = {
      id: 'folder-1',
      name: '测试',
      parent_id: null,
      children: [],
      created_at: '2026-03-22T00:00:00Z',
    }
    mocks.folderGetTree.mockResolvedValue({ data: [folder] })
    mocks.documentList.mockResolvedValue({ data: [] })
    mocks.documentUpload.mockRejectedValue(new AxiosError(
      'Request failed with status code 400',
      '400',
      undefined,
      undefined,
      {
        data: { detail: 'PDF 解析依赖缺失: mineru' },
        status: 400,
        statusText: 'Bad Request',
        headers: {},
        config: {} as never,
      },
    ))

    const view = useKnowledgeView()
    await view.enterFolder(folder)

    const file = new File(['%PDF-1.4'], 'spec.pdf', { type: 'application/pdf' })
    const event = { target: { files: [file], value: 'spec.pdf' } } as unknown as Event
    await view.handleUpload(event)

    expect(view.uploadError.value).toBe('PDF 解析依赖缺失: mineru')
  })
})
