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

// --- Mocks (must be before imports) ---

const mockEventSave = vi.fn()
function MockEvent(data) {
  Object.assign(this, data)
  this._id = 'evt-123'
  this.slug = '2026-04-01-test-event'
  this.save = mockEventSave.mockResolvedValue(this)
}
MockEvent.findByIdAndUpdate = vi.fn()

vi.mock('../../../server/models/event.js', () => ({ default: MockEvent }))

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

vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() }))
vi.mock('../../../server/utils/auth.js', () => ({
  requireAdmin: vi.fn().mockResolvedValue({ email: 'admin@example.com', role: 'admin' })
}))
vi.mock('../../../server/utils/validateBody.js', () => ({ validateBody: vi.fn() }))
vi.mock('../../../server/utils/schemas.js', () => ({
  adminEventCreateSchema: {},
  adminEventUpdateSchema: {}
}))

import Tag from '../../../server/models/tag.js'
import { validateBody } from '../../../server/utils/validateBody.js'
import { validateTagSlugs } from '../../../server/utils/validateTagSlugs.js'
import createHandler from '../../../server/api/admin/events.post.js'
import updateHandler from '../../../server/api/admin/events/[id].put.js'
import { createMockEvent } from '../helpers/createMockEvent.js'

// Stub the global adminEventUpdateSchema used by the update route (auto-imported by Nitro)
vi.stubGlobal('adminEventUpdateSchema', {})

// --- Helpers ---

const validEventBody = {
  title: 'Test Event',
  description: 'A test event',
  startDate: '2026-04-01T10:00:00Z',
  endDate: '2026-04-01T12:00:00Z',
  location: 'https://meet.example.com'
}

// --- Tests ---

describe('Event tag validation', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    // Override global stubs (from setup.js) to use the real/mocked implementations
    globalThis.validateBody = validateBody
    globalThis.validateTagSlugs = validateTagSlugs
  })

  describe('create route (events.post)', () => {
    it('accepts valid tags', async () => {
      validateBody.mockResolvedValue({
        ...validEventBody,
        tags: ['game-design', 'cooperatives']
      })
      Tag.find.mockResolvedValue([
        { slug: 'game-design' },
        { slug: 'cooperatives' }
      ])

      const event = createMockEvent({ method: 'POST', path: '/api/admin/events' })
      const result = await createHandler(event)

      expect(Tag.find).toHaveBeenCalledWith({ slug: { $in: ['game-design', 'cooperatives'] } })
      expect(result.tags).toEqual(['game-design', 'cooperatives'])
    })

    it('rejects unknown tag slugs with 400', async () => {
      validateBody.mockResolvedValue({
        ...validEventBody,
        tags: ['game-design', 'nonexistent-tag']
      })
      Tag.find.mockResolvedValue([
        { slug: 'game-design' }
      ])

      const event = createMockEvent({ method: 'POST', path: '/api/admin/events' })

      await expect(createHandler(event)).rejects.toMatchObject({
        statusCode: 400,
        statusMessage: 'Unknown tag slugs: nonexistent-tag'
      })
    })

    it('skips validation when tags are omitted', async () => {
      validateBody.mockResolvedValue({ ...validEventBody })

      const event = createMockEvent({ method: 'POST', path: '/api/admin/events' })
      await createHandler(event)

      expect(Tag.find).not.toHaveBeenCalled()
    })

    it('skips validation when tags array is empty', async () => {
      validateBody.mockResolvedValue({ ...validEventBody, tags: [] })

      const event = createMockEvent({ method: 'POST', path: '/api/admin/events' })
      await createHandler(event)

      expect(Tag.find).not.toHaveBeenCalled()
    })
  })

  describe('update route (events/[id].put)', () => {
    beforeEach(() => {
      MockEvent.findByIdAndUpdate.mockResolvedValue({
        _id: 'evt-123',
        ...validEventBody,
        tags: ['game-design']
      })
    })

    it('accepts valid tags', async () => {
      validateBody.mockResolvedValue({
        ...validEventBody,
        tags: ['game-design']
      })
      Tag.find.mockResolvedValue([
        { slug: 'game-design' }
      ])

      const event = createMockEvent({ method: 'PUT', path: '/api/admin/events/evt-123' })
      event.context = { ...event.context, params: { id: 'evt-123' } }

      const result = await updateHandler(event)

      expect(Tag.find).toHaveBeenCalledWith({ slug: { $in: ['game-design'] } })
      expect(result.tags).toEqual(['game-design'])
    })

    it('rejects unknown tag slugs with 400', async () => {
      validateBody.mockResolvedValue({
        ...validEventBody,
        tags: ['valid-tag', 'bogus-tag', 'also-bogus']
      })
      Tag.find.mockResolvedValue([
        { slug: 'valid-tag' }
      ])

      const event = createMockEvent({ method: 'PUT', path: '/api/admin/events/evt-123' })
      event.context = { ...event.context, params: { id: 'evt-123' } }

      await expect(updateHandler(event)).rejects.toMatchObject({
        statusCode: 400,
        statusMessage: 'Unknown tag slugs: bogus-tag, also-bogus'
      })
    })
  })
})
