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

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

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

import { requireAuth } from '../../../server/utils/auth.js'
import Member from '../../../server/models/member.js'
import profilePatchHandler from '../../../server/api/members/profile.patch.js'
import { createMockEvent } from '../helpers/createMockEvent.js'

describe('members profile PATCH endpoint', () => {
  const mockMember = {
    _id: 'member-123',
    email: 'test@example.com',
    name: 'Test User',
    circle: 'community',
    contributionAmount: 5,
    pronouns: 'they/them',
    timeZone: 'America/New_York',
    avatar: 'https://example.com/avatar.jpg',
    studio: 'Test Studio',
    bio: 'Updated bio',
    location: 'NYC',
    socialLinks: { twitter: '@test' },
    showInDirectory: true
  }

  beforeEach(() => {
    vi.clearAllMocks()
    requireAuth.mockResolvedValue({ _id: 'member-123' })
    Member.findByIdAndUpdate.mockResolvedValue(mockMember)
  })

  describe('field allowlist - forbidden fields are rejected', () => {
    it('does not pass helcimCustomerId to database update', async () => {
      const event = createMockEvent({
        method: 'PATCH',
        path: '/api/members/profile',
        body: { bio: 'new bio', helcimCustomerId: 'hacked-id' }
      })

      await profilePatchHandler(event)

      const updateCall = Member.findByIdAndUpdate.mock.calls[0]
      const setData = updateCall[1].$set
      expect(setData).not.toHaveProperty('helcimCustomerId')
      expect(setData).toHaveProperty('bio', 'new bio')
    })

    it('does not pass role to database update', async () => {
      const event = createMockEvent({
        method: 'PATCH',
        path: '/api/members/profile',
        body: { bio: 'new bio', role: 'admin' }
      })

      await profilePatchHandler(event)

      const setData = Member.findByIdAndUpdate.mock.calls[0][1].$set
      expect(setData).not.toHaveProperty('role')
    })

    it('does not pass status to database update', async () => {
      const event = createMockEvent({
        method: 'PATCH',
        path: '/api/members/profile',
        body: { bio: 'new bio', status: 'active' }
      })

      await profilePatchHandler(event)

      const setData = Member.findByIdAndUpdate.mock.calls[0][1].$set
      expect(setData).not.toHaveProperty('status')
    })

    it('does not pass email to database update', async () => {
      const event = createMockEvent({
        method: 'PATCH',
        path: '/api/members/profile',
        body: { bio: 'new bio', email: 'hacked@evil.com' }
      })

      await profilePatchHandler(event)

      const setData = Member.findByIdAndUpdate.mock.calls[0][1].$set
      expect(setData).not.toHaveProperty('email')
    })

    it('does not pass _id to database update', async () => {
      const event = createMockEvent({
        method: 'PATCH',
        path: '/api/members/profile',
        body: { bio: 'new bio', _id: 'different-id' }
      })

      await profilePatchHandler(event)

      const setData = Member.findByIdAndUpdate.mock.calls[0][1].$set
      expect(setData).not.toHaveProperty('_id')
    })
  })

  describe('field allowlist - allowed fields pass through', () => {
    it('passes allowed profile fields through', async () => {
      const event = createMockEvent({
        method: 'PATCH',
        path: '/api/members/profile',
        body: {
          pronouns: 'they/them',
          bio: 'Updated bio',
          studio: 'Test Studio',
          location: 'NYC',
          timeZone: 'America/New_York',
          avatar: 'https://example.com/avatar.jpg',
          showInDirectory: true,
          socialLinks: { twitter: '@test' }
        }
      })

      await profilePatchHandler(event)

      const setData = Member.findByIdAndUpdate.mock.calls[0][1].$set
      expect(setData).toHaveProperty('pronouns', 'they/them')
      expect(setData).toHaveProperty('bio', 'Updated bio')
      expect(setData).toHaveProperty('studio', 'Test Studio')
      expect(setData).toHaveProperty('location', 'NYC')
      expect(setData).toHaveProperty('timeZone', 'America/New_York')
      expect(setData).toHaveProperty('avatar', 'https://example.com/avatar.jpg')
      expect(setData).toHaveProperty('showInDirectory', true)
      expect(setData).toHaveProperty('socialLinks')
    })

  })
})
