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

const { mockUserUpdate, mockGetIronSession } = vi.hoisted(() => ({
  mockUserUpdate: vi.fn(),
  mockGetIronSession: vi.fn(),
}))

vi.mock('next/cache', () => ({ revalidatePath: vi.fn() }))
vi.mock('next/headers', () => ({ cookies: vi.fn().mockResolvedValue({}) }))
vi.mock('iron-session', () => ({ getIronSession: mockGetIronSession }))
vi.mock('@/lib/session', () => ({ sessionOptions: { cookieName: 'test', password: 'test' } }))
vi.mock('@/lib/prisma', () => ({
  prisma: { user: { update: mockUserUpdate } },
}))

import { updateMinQuotaPctAction } from '@/actions/settings'

const SESSION_USER = { userId: 'user-1', isDemo: false }
const SESSION_DEMO = { userId: 'demo-1', isDemo: true }
const SESSION_UNAUTH = { userId: undefined, isDemo: false }

describe('updateMinQuotaPctAction', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockUserUpdate.mockResolvedValue({})
  })

  it('returns error when not authenticated', async () => {
    mockGetIronSession.mockResolvedValue(SESSION_UNAUTH)
    const result = await updateMinQuotaPctAction(20)
    expect(result).toMatchObject({ error: expect.any(String) })
    expect(mockUserUpdate).not.toHaveBeenCalled()
  })

  it('returns 403 error for demo session', async () => {
    mockGetIronSession.mockResolvedValue(SESSION_DEMO)
    const result = await updateMinQuotaPctAction(20)
    expect(result).toMatchObject({ status: 403 })
    expect(mockUserUpdate).not.toHaveBeenCalled()
  })

  it('returns 422 error when value is 0 (below min)', async () => {
    mockGetIronSession.mockResolvedValue(SESSION_USER)
    const result = await updateMinQuotaPctAction(0)
    expect(result).toMatchObject({ status: 422 })
    expect(mockUserUpdate).not.toHaveBeenCalled()
  })

  it('returns 422 error when value is 101 (above max)', async () => {
    mockGetIronSession.mockResolvedValue(SESSION_USER)
    const result = await updateMinQuotaPctAction(101)
    expect(result).toMatchObject({ status: 422 })
    expect(mockUserUpdate).not.toHaveBeenCalled()
  })

  it('saves valid value and returns success', async () => {
    mockGetIronSession.mockResolvedValue(SESSION_USER)
    const result = await updateMinQuotaPctAction(35)
    expect(result).toEqual({ success: true })
    expect(mockUserUpdate).toHaveBeenCalledWith({
      where: { id: 'user-1' },
      data: { min_quota_pct: 35 },
    })
  })

  it('accepts boundary values 1 and 100', async () => {
    mockGetIronSession.mockResolvedValue(SESSION_USER)
    await updateMinQuotaPctAction(1)
    await updateMinQuotaPctAction(100)
    expect(mockUserUpdate).toHaveBeenCalledTimes(2)
  })
})
