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

vi.mock('next/headers', () => ({ cookies: vi.fn().mockResolvedValue({}) }))
vi.mock('iron-session', () => ({
  getIronSession: vi.fn().mockResolvedValue({ userId: 'user-1', isDemo: false }),
}))
vi.mock('@/lib/session', () => ({
  sessionOptions: { cookieName: 'test', password: 'test' },
}))
vi.mock('@/lib/prisma', () => ({
  prisma: {
    user: { findUnique: vi.fn() },
    $transaction: vi.fn(async (fn: (tx: unknown) => Promise<unknown>) => {
      return fn({
        user: {
          findUnique: vi.fn().mockResolvedValue({ settings: {} }),
          update: vi.fn().mockResolvedValue({}),
        },
      })
    }),
    $executeRaw: vi.fn().mockResolvedValue(1),
  },
}))

import { prisma } from '@/lib/prisma'
import { getIronSession } from 'iron-session'
import { updateUserSettingsAction } from '@/actions/user-settings'

const mockPrisma = prisma as unknown as {
  user: { findUnique: ReturnType<typeof vi.fn> }
  $transaction: ReturnType<typeof vi.fn>
  $executeRaw: ReturnType<typeof vi.fn>
}
const mockGetIronSession = getIronSession as ReturnType<typeof vi.fn>

beforeEach(() => {
  vi.clearAllMocks()
  mockGetIronSession.mockResolvedValue({ userId: 'user-1', isDemo: false })
  mockPrisma.$executeRaw.mockResolvedValue(1)
})

describe('updateUserSettingsAction', () => {
  it('returns 401 when not logged in', async () => {
    mockGetIronSession.mockResolvedValue({ userId: undefined, isDemo: false })
    const result = await updateUserSettingsAction({})
    expect(result).toEqual({ error: 'Niet ingelogd', code: 401 })
  })

  it('returns 403 for demo accounts', async () => {
    mockGetIronSession.mockResolvedValue({ userId: 'user-1', isDemo: true })
    const result = await updateUserSettingsAction({})
    expect('error' in result && result.code).toBe(403)
  })

  it('returns 422 when patch is invalid', async () => {
    const result = await updateUserSettingsAction({
      views: { sprintBacklog: { filterStatus: 'NONSENSE' } },
    } as never)
    expect('error' in result && result.code).toBe(422)
  })

  it('merges with current settings and emits notify on success', async () => {
    const existingFindUnique = vi.fn().mockResolvedValue({
      settings: { views: { sprintBacklog: { sort: 'code' } } },
    })
    const update = vi.fn().mockResolvedValue({})
    mockPrisma.$transaction.mockImplementationOnce(async (fn: (tx: unknown) => Promise<unknown>) => {
      return fn({ user: { findUnique: existingFindUnique, update } })
    })

    const result = await updateUserSettingsAction({
      views: { sprintBacklog: { sortDir: 'desc' } },
    })

    expect('success' in result && result.success).toBe(true)
    expect(update).toHaveBeenCalledWith({
      where: { id: 'user-1' },
      data: { settings: { views: { sprintBacklog: { sort: 'code', sortDir: 'desc' } } } },
    })
    expect(mockPrisma.$executeRaw).toHaveBeenCalled()
  })
})
