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

vi.mock('next/cache', () => ({ revalidatePath: vi.fn() }))
vi.mock('next/headers', () => ({
  cookies: vi.fn().mockResolvedValue({
    set: vi.fn(),
    get: vi.fn(),
    delete: vi.fn(),
  }),
}))
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/product-access', () => ({
  productAccessFilter: vi.fn().mockReturnValue({}),
}))
vi.mock('@/lib/prisma', () => ({
  prisma: {
    product: { findFirst: vi.fn() },
    user: {
      findUnique: vi.fn(),
      update: vi.fn().mockResolvedValue({}),
    },
    $executeRaw: vi.fn().mockResolvedValue(1),
  },
}))

import { prisma } from '@/lib/prisma'
import {
  clearPendingSprintDraftAction,
  setPendingSprintDraftAction,
} from '@/actions/sprint-draft'
import type { PendingSprintDraft, UserSettings } from '@/lib/user-settings'

const mockPrisma = prisma as unknown as {
  product: { findFirst: ReturnType<typeof vi.fn> }
  user: {
    findUnique: ReturnType<typeof vi.fn>
    update: ReturnType<typeof vi.fn>
  }
}

const validDraft: PendingSprintDraft = {
  goal: 'Sprint 1',
  pbiIntent: { pbiA: 'all' },
  storyOverrides: { pbiA: { add: [], remove: ['story-1'] } },
}

describe('setPendingSprintDraftAction', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockPrisma.product.findFirst.mockReset()
    mockPrisma.user.findUnique.mockReset()
    mockPrisma.user.update.mockReset().mockResolvedValue({})
  })

  it('persists draft for accessible product', async () => {
    mockPrisma.product.findFirst.mockResolvedValueOnce({ id: 'p1' })
    mockPrisma.user.findUnique.mockResolvedValueOnce({ settings: {} })

    const result = await setPendingSprintDraftAction('p1', validDraft)

    expect(result).toEqual({ success: true })
    const updateArg = mockPrisma.user.update.mock.calls[0][0] as {
      data: { settings: UserSettings }
    }
    expect(updateArg.data.settings.workflow?.pendingSprintDraft?.p1).toMatchObject({
      goal: 'Sprint 1',
      pbiIntent: { pbiA: 'all' },
    })
  })

  it('preserves drafts for other products', async () => {
    mockPrisma.product.findFirst.mockResolvedValueOnce({ id: 'p1' })
    mockPrisma.user.findUnique.mockResolvedValueOnce({
      settings: {
        workflow: {
          pendingSprintDraft: {
            p2: { goal: 'P2 draft', pbiIntent: {}, storyOverrides: {} },
          },
        },
      },
    })

    await setPendingSprintDraftAction('p1', validDraft)

    const updateArg = mockPrisma.user.update.mock.calls[0][0] as {
      data: { settings: UserSettings }
    }
    const drafts = updateArg.data.settings.workflow?.pendingSprintDraft
    expect(Object.keys(drafts ?? {})).toEqual(expect.arrayContaining(['p1', 'p2']))
  })

  it('rejects invalid draft (empty goal)', async () => {
    mockPrisma.product.findFirst.mockResolvedValueOnce({ id: 'p1' })

    const result = await setPendingSprintDraftAction('p1', {
      ...validDraft,
      goal: '',
    } as PendingSprintDraft)

    expect(result).toHaveProperty('error')
    expect(mockPrisma.user.update).not.toHaveBeenCalled()
  })

  it('rejects when product not accessible', async () => {
    mockPrisma.product.findFirst.mockResolvedValueOnce(null)

    const result = await setPendingSprintDraftAction('p1', validDraft)

    expect(result).toEqual({ error: 'Product niet gevonden of niet toegankelijk' })
    expect(mockPrisma.user.update).not.toHaveBeenCalled()
  })
})

describe('clearPendingSprintDraftAction', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockPrisma.product.findFirst.mockReset()
    mockPrisma.user.findUnique.mockReset()
    mockPrisma.user.update.mockReset().mockResolvedValue({})
  })

  it('removes draft key for product', async () => {
    mockPrisma.product.findFirst.mockResolvedValueOnce({ id: 'p1' })
    mockPrisma.user.findUnique.mockResolvedValueOnce({
      settings: {
        workflow: {
          pendingSprintDraft: {
            p1: { goal: 'gone', pbiIntent: {}, storyOverrides: {} },
            p2: { goal: 'keep', pbiIntent: {}, storyOverrides: {} },
          },
        },
      },
    })

    await clearPendingSprintDraftAction('p1')

    const updateArg = mockPrisma.user.update.mock.calls[0][0] as {
      data: { settings: UserSettings }
    }
    expect(updateArg.data.settings.workflow?.pendingSprintDraft).toEqual({
      p2: { goal: 'keep', pbiIntent: {}, storyOverrides: {} },
    })
  })

  it('is a no-op when there is no draft for the product', async () => {
    mockPrisma.product.findFirst.mockResolvedValueOnce({ id: 'p1' })
    mockPrisma.user.findUnique.mockResolvedValueOnce({ settings: {} })

    const result = await clearPendingSprintDraftAction('p1')

    expect(result).toEqual({ success: true })
    expect(mockPrisma.user.update).not.toHaveBeenCalled()
  })

  it('rejects when product not accessible', async () => {
    mockPrisma.product.findFirst.mockResolvedValueOnce(null)

    const result = await clearPendingSprintDraftAction('p1')

    expect(result).toEqual({ error: 'Product niet gevonden of niet toegankelijk' })
  })
})
