/**
 * Tests for knowledge base document upsert API route
 *
 * @vitest-environment node
 */
import {
  auditMock,
  createMockRequest,
  hybridAuthMock,
  hybridAuthMockFns,
  knowledgeApiUtilsMock,
} from '@sim/testing'
import { beforeEach, describe, expect, it, vi } from 'vitest'

const { mockDbChain } = vi.hoisted(() => {
  const chain = {
    select: vi.fn().mockReturnThis(),
    from: vi.fn().mockReturnThis(),
    where: vi.fn().mockReturnThis(),
    limit: vi.fn().mockResolvedValue([]),
  }
  return { mockDbChain: chain }
})

vi.mock('@sim/db', () => ({ db: mockDbChain }))
vi.mock('@/lib/auth/hybrid', () => hybridAuthMock)
vi.mock('@/app/api/knowledge/utils', () => knowledgeApiUtilsMock)
vi.mock('@sim/audit', () => auditMock)

vi.mock('@/lib/billing/calculations/usage-monitor', () => ({
  checkActorUsageLimits: vi.fn().mockResolvedValue({ isExceeded: false }),
}))

vi.mock('@/lib/knowledge/documents/service', () => ({
  createDocumentRecords: vi.fn(),
  deleteDocument: vi.fn(),
  getProcessingConfig: vi.fn().mockReturnValue({ maxConcurrentDocuments: 1, batchSize: 1 }),
  processDocumentsWithQueue: vi.fn(),
  KnowledgeBaseFileOwnershipError: class KnowledgeBaseFileOwnershipError extends Error {},
}))

import { createDocumentRecords, processDocumentsWithQueue } from '@/lib/knowledge/documents/service'
import { POST } from '@/app/api/knowledge/[id]/documents/upsert/route'
import { checkKnowledgeBaseWriteAccess } from '@/app/api/knowledge/utils'

describe('POST /api/knowledge/[id]/documents/upsert', () => {
  const params = Promise.resolve({ id: 'kb-123' })

  beforeEach(() => {
    vi.clearAllMocks()
    mockDbChain.select.mockReturnThis()
    mockDbChain.from.mockReturnThis()
    mockDbChain.where.mockReturnThis()
    mockDbChain.limit.mockResolvedValue([])

    hybridAuthMockFns.mockCheckSessionOrInternalAuth.mockResolvedValue({
      success: true,
      userId: 'user-1',
      authType: 'session',
      userName: 'Test User',
      userEmail: 'test@example.com',
    })

    vi.mocked(checkKnowledgeBaseWriteAccess).mockResolvedValue({
      hasAccess: true,
      knowledgeBase: { id: 'kb-123', userId: 'user-1', workspaceId: 'ws-1', name: 'KB' },
    } as any)

    vi.mocked(createDocumentRecords).mockResolvedValue([
      { documentId: 'doc-new', filename: 'note.txt' },
    ] as any)
    vi.mocked(processDocumentsWithQueue).mockResolvedValue(undefined as any)
  })

  const baseBody = {
    filename: 'note.txt',
    fileSize: 11,
    mimeType: 'text/plain',
  }

  it('accepts a data: URI', async () => {
    const req = createMockRequest('POST', {
      ...baseBody,
      fileUrl: 'data:text/plain;base64,SGVsbG8gd29ybGQ=',
    })
    const res = await POST(req, { params })
    expect(res.status).toBe(200)
    expect(createDocumentRecords).toHaveBeenCalled()
  })

  it('accepts an https URL', async () => {
    const req = createMockRequest('POST', {
      ...baseBody,
      fileUrl: 'https://example.com/note.txt',
    })
    const res = await POST(req, { params })
    expect(res.status).toBe(200)
    expect(createDocumentRecords).toHaveBeenCalled()
  })

  it.each([
    ['absolute local path', '/etc/passwd'],
    ['app config path', '/app/.env'],
    ['file:// URL', 'file:///etc/passwd'],
    ['relative serve path', '/api/files/serve/kb/foo.pdf'],
    ['ftp URL', 'ftp://example.com/file.pdf'],
    ['parent traversal', '../../etc/passwd'],
    ['windows path', 'C:\\Windows\\System32\\config\\SAM'],
  ])('rejects %s with 400 and never invokes the pipeline', async (_label, fileUrl) => {
    const req = createMockRequest('POST', { ...baseBody, fileUrl })
    const res = await POST(req, { params })
    expect(res.status).toBe(400)
    expect(createDocumentRecords).not.toHaveBeenCalled()
    expect(processDocumentsWithQueue).not.toHaveBeenCalled()
  })
})
