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

const { mockFind, mockSort, mockLimit, mockSelect, mockLean } = vi.hoisted(() => ({
  mockFind: vi.fn(),
  mockSort: vi.fn(),
  mockLimit: vi.fn(),
  mockSelect: vi.fn(),
  mockLean: vi.fn()
}))

vi.mock('../../../server/models/wikiArticle.js', () => ({
  default: { find: mockFind }
}))

vi.mock('../../../server/utils/mongoose.js', () => ({
  connectDB: vi.fn()
}))

vi.mock('../../../server/utils/auth.js', () => ({
  requireAuth: vi.fn()
}))

import { requireAuth } from '../../../server/utils/auth.js'
import handler from '../../../server/api/wiki/recommended.get.js'
import { createMockEvent } from '../helpers/createMockEvent.js'

// Wire up the chained query builder
function setupChain(result = []) {
  mockLean.mockResolvedValue(result)
  mockSelect.mockReturnValue({ lean: mockLean })
  mockLimit.mockReturnValue({ select: mockSelect })
  mockSort.mockReturnValue({ limit: mockLimit })
  mockFind.mockReturnValue({ sort: mockSort })
}

function makeMember(overrides = {}) {
  return {
    _id: 'member-1',
    craftTags: [],
    board: { topics: [] },
    ...overrides
  }
}

function makeArticle(overrides = {}) {
  return {
    _id: 'article-1',
    title: 'Test Article',
    url: 'https://wiki.example.com/test-article',
    summary: 'A test article',
    tags: ['game-design'],
    collection: 'Guides',
    publishedAt: new Date('2026-04-01'),
    ...overrides
  }
}

describe('GET /api/wiki/recommended', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('returns articles matching member tags', async () => {
    const member = makeMember({ craftTags: ['game-design', 'narrative'] })
    requireAuth.mockResolvedValue(member)

    const articles = [makeArticle({ tags: ['game-design'] })]
    setupChain(articles)

    const event = createMockEvent({ method: 'GET', path: '/api/wiki/recommended' })
    const result = await handler(event)

    expect(result).toEqual(articles)
    expect(mockFind).toHaveBeenCalledWith(
      expect.objectContaining({
        tags: { $in: expect.arrayContaining(['game-design', 'narrative']) }
      })
    )
  })

  it('returns empty array when no tag overlap', async () => {
    const member = makeMember({ craftTags: ['audio'] })
    requireAuth.mockResolvedValue(member)

    setupChain([])

    const event = createMockEvent({ method: 'GET', path: '/api/wiki/recommended' })
    const result = await handler(event)

    expect(result).toEqual([])
  })

  it('excludes unpublished articles (publishedAt: null)', async () => {
    const member = makeMember({ craftTags: ['game-design'] })
    requireAuth.mockResolvedValue(member)

    setupChain([])

    const event = createMockEvent({ method: 'GET', path: '/api/wiki/recommended' })
    await handler(event)

    const filter = mockFind.mock.calls[0][0]
    expect(filter.publishedAt).toEqual({ $ne: null })
  })

  it('uses default limit of 10', async () => {
    const member = makeMember({ craftTags: ['game-design'] })
    requireAuth.mockResolvedValue(member)

    setupChain([])

    const event = createMockEvent({ method: 'GET', path: '/api/wiki/recommended' })
    await handler(event)

    expect(mockLimit).toHaveBeenCalledWith(10)
  })

  it('accepts limit query param', async () => {
    const member = makeMember({ craftTags: ['game-design'] })
    requireAuth.mockResolvedValue(member)

    setupChain([])

    const event = createMockEvent({ method: 'GET', path: '/api/wiki/recommended?limit=5' })
    await handler(event)

    expect(mockLimit).toHaveBeenCalledWith(5)
  })

  it('caps limit at 25', async () => {
    const member = makeMember({ craftTags: ['game-design'] })
    requireAuth.mockResolvedValue(member)

    setupChain([])

    const event = createMockEvent({ method: 'GET', path: '/api/wiki/recommended?limit=100' })
    await handler(event)

    expect(mockLimit).toHaveBeenCalledWith(25)
  })

  it('returns empty array when member has no tags', async () => {
    const member = makeMember()
    requireAuth.mockResolvedValue(member)

    const event = createMockEvent({ method: 'GET', path: '/api/wiki/recommended' })
    const result = await handler(event)

    expect(result).toEqual([])
    // Should not query the database at all
    expect(mockFind).not.toHaveBeenCalled()
  })

  it('requires auth (401)', async () => {
    requireAuth.mockRejectedValue(
      createError({ statusCode: 401, statusMessage: 'Unauthorized' })
    )

    const event = createMockEvent({ method: 'GET', path: '/api/wiki/recommended' })

    await expect(handler(event)).rejects.toMatchObject({
      statusCode: 401
    })
  })
})
