import { describe, it, expect, beforeEach } from 'vitest'
import { createMockEvent } from '../helpers/createMockEvent.js'

// Import the handler — defineEventHandler is a global passthrough,
// so the default export is the raw handler function.
import csrfMiddleware from '../../../server/middleware/01.csrf.js'

describe('CSRF middleware', () => {
  describe('safe methods bypass', () => {
    it.each(['GET', 'HEAD', 'OPTIONS'])('%s requests pass through', (method) => {
      const event = createMockEvent({ method, path: '/api/test' })
      expect(() => csrfMiddleware(event)).not.toThrow()
    })
  })

  describe('non-API paths bypass', () => {
    it('POST to non-/api/ path passes through', () => {
      const event = createMockEvent({ method: 'POST', path: '/some/page' })
      expect(() => csrfMiddleware(event)).not.toThrow()
    })
  })

  describe('exempt routes bypass', () => {
    it.each([
      '/api/helcim/webhook',
      '/api/slack/webhook',
      '/api/auth/verify'
    ])('POST to %s passes through', (path) => {
      const event = createMockEvent({ method: 'POST', path })
      expect(() => csrfMiddleware(event)).not.toThrow()
    })
  })

  describe('CSRF enforcement on state-changing API requests', () => {
    it('throws 403 when POST to /api/* has no x-csrf-token header', () => {
      const event = createMockEvent({
        method: 'POST',
        path: '/api/members/profile',
        cookies: { 'csrf-token': 'abc123' }
      })

      expect(() => csrfMiddleware(event)).toThrowError(
        expect.objectContaining({
          statusCode: 403,
          statusMessage: 'CSRF token missing or invalid'
        })
      )
    })

    it('throws 403 when header and cookie tokens do not match', () => {
      const event = createMockEvent({
        method: 'POST',
        path: '/api/members/profile',
        cookies: { 'csrf-token': 'cookie-token' },
        headers: { 'x-csrf-token': 'different-token' }
      })

      expect(() => csrfMiddleware(event)).toThrowError(
        expect.objectContaining({
          statusCode: 403,
          statusMessage: 'CSRF token missing or invalid'
        })
      )
    })

    it('passes when header and cookie tokens match', () => {
      const token = 'matching-token-value'
      const event = createMockEvent({
        method: 'POST',
        path: '/api/members/profile',
        cookies: { 'csrf-token': token },
        headers: { 'x-csrf-token': token }
      })

      expect(() => csrfMiddleware(event)).not.toThrow()
    })

    it('enforces CSRF on DELETE requests', () => {
      const event = createMockEvent({
        method: 'DELETE',
        path: '/api/admin/events/123',
        cookies: { 'csrf-token': 'abc' }
      })

      expect(() => csrfMiddleware(event)).toThrowError(
        expect.objectContaining({ statusCode: 403 })
      )
    })

    it('enforces CSRF on PATCH requests', () => {
      const event = createMockEvent({
        method: 'PATCH',
        path: '/api/members/profile',
        cookies: { 'csrf-token': 'abc' }
      })

      expect(() => csrfMiddleware(event)).toThrowError(
        expect.objectContaining({ statusCode: 403 })
      )
    })
  })

  describe('cookie provisioning', () => {
    it('sets csrf-token cookie when none exists', () => {
      const event = createMockEvent({ method: 'GET', path: '/api/test' })
      csrfMiddleware(event)

      // The middleware calls setCookie which sets a Set-Cookie header
      const setCookieHeader = event._testSetHeaders['set-cookie']
      expect(setCookieHeader).toBeDefined()
      expect(setCookieHeader).toContain('csrf-token=')
    })

    it('does not set a new cookie when one already exists', () => {
      const event = createMockEvent({
        method: 'GET',
        path: '/api/test',
        cookies: { 'csrf-token': 'existing-token' }
      })
      csrfMiddleware(event)

      const setCookieHeader = event._testSetHeaders['set-cookie']
      // Should not have set a new cookie
      expect(setCookieHeader).toBeUndefined()
    })
  })
})
