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

vi.mock('@/lib/prisma', () => ({
  prisma: {
    sprint: { findFirst: vi.fn() },
    user: {
      findUnique: vi.fn(),
      update: vi.fn().mockResolvedValue({}),
    },
    $executeRaw: vi.fn().mockResolvedValue(1),
  },
}))

import { prisma } from '@/lib/prisma'
import type { UserSettings } from '@/lib/user-settings'
import {
  clearActiveSprintInSettings,
  readStoredActiveSprintState,
  resolveActiveSprint,
} from '@/lib/active-sprint'

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

function withSettings(settings: UserSettings) {
  mockPrisma.user.findUnique.mockResolvedValueOnce({ settings })
}

describe('readStoredActiveSprintState', () => {
  it('returns unset when activeSprints map is absent', () => {
    expect(readStoredActiveSprintState({}, 'p1')).toEqual({ kind: 'unset' })
  })

  it('returns unset when productId key is absent', () => {
    const settings: UserSettings = {
      layout: { activeSprints: { p2: 'sprint-2' } },
    }
    expect(readStoredActiveSprintState(settings, 'p1')).toEqual({
      kind: 'unset',
    })
  })

  it('returns cleared when key is present with null value', () => {
    const settings: UserSettings = {
      layout: { activeSprints: { p1: null } },
    }
    expect(readStoredActiveSprintState(settings, 'p1')).toEqual({
      kind: 'cleared',
    })
  })

  it('returns set when key is present with string value', () => {
    const settings: UserSettings = {
      layout: { activeSprints: { p1: 'sprint-1' } },
    }
    expect(readStoredActiveSprintState(settings, 'p1')).toEqual({
      kind: 'set',
      sprintId: 'sprint-1',
    })
  })
})

describe('resolveActiveSprint', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('returns null without fallback when key is explicitly null (cleared)', async () => {
    withSettings({ layout: { activeSprints: { p1: null } } })

    const result = await resolveActiveSprint('p1', 'user-1')

    expect(result).toBeNull()
    expect(mockPrisma.sprint.findFirst).not.toHaveBeenCalled()
  })

  it('returns the stored sprint when key is set and sprint exists', async () => {
    withSettings({ layout: { activeSprints: { p1: 'sprint-1' } } })
    mockPrisma.sprint.findFirst.mockResolvedValueOnce({
      id: 'sprint-1',
      code: 'SP-1',
      status: 'OPEN',
    })

    const result = await resolveActiveSprint('p1', 'user-1')

    expect(result).toEqual({ id: 'sprint-1', code: 'SP-1', status: 'OPEN' })
    expect(mockPrisma.sprint.findFirst).toHaveBeenCalledTimes(1)
  })

  it('falls back when stored sprint is not found in DB', async () => {
    withSettings({ layout: { activeSprints: { p1: 'stale-id' } } })
    mockPrisma.sprint.findFirst
      .mockResolvedValueOnce(null) // stored lookup misses
      .mockResolvedValueOnce({ id: 'sprint-open', code: 'SP-O', status: 'OPEN' })

    const result = await resolveActiveSprint('p1', 'user-1')

    expect(result).toEqual({
      id: 'sprint-open',
      code: 'SP-O',
      status: 'OPEN',
    })
  })

  it('falls back to first OPEN sprint when key is absent', async () => {
    withSettings({})
    mockPrisma.sprint.findFirst.mockResolvedValueOnce({
      id: 'sprint-open',
      code: 'SP-O',
      status: 'OPEN',
    })

    const result = await resolveActiveSprint('p1', 'user-1')

    expect(result).toEqual({
      id: 'sprint-open',
      code: 'SP-O',
      status: 'OPEN',
    })
  })

  it('falls back to recent CLOSED sprint when no OPEN exists', async () => {
    withSettings({})
    mockPrisma.sprint.findFirst
      .mockResolvedValueOnce(null) // no OPEN
      .mockResolvedValueOnce({
        id: 'sprint-closed',
        code: 'SP-C',
        status: 'CLOSED',
      })

    const result = await resolveActiveSprint('p1', 'user-1')

    expect(result).toEqual({
      id: 'sprint-closed',
      code: 'SP-C',
      status: 'CLOSED',
    })
  })

  it('returns null when key absent and no sprints exist', async () => {
    withSettings({})
    mockPrisma.sprint.findFirst.mockResolvedValue(null)

    const result = await resolveActiveSprint('p1', 'user-1')

    expect(result).toBeNull()
  })
})

describe('clearActiveSprintInSettings', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('writes null instead of deleting the key', async () => {
    withSettings({
      layout: { activeSprints: { p1: 'sprint-1', p2: 'sprint-2' } },
    })

    await clearActiveSprintInSettings('user-1', 'p1')

    expect(mockPrisma.user.update).toHaveBeenCalledTimes(1)
    const updateArg = mockPrisma.user.update.mock.calls[0][0] as {
      data: { settings: UserSettings }
    }
    expect(updateArg.data.settings.layout?.activeSprints).toEqual({
      p1: null,
      p2: 'sprint-2',
    })
  })

  it('adds the key with null when previously unset', async () => {
    withSettings({})

    await clearActiveSprintInSettings('user-1', 'p1')

    const updateArg = mockPrisma.user.update.mock.calls[0][0] as {
      data: { settings: UserSettings }
    }
    expect(updateArg.data.settings.layout?.activeSprints).toEqual({ p1: null })
  })
})
