import type {
  Prisma,
  PrismaClient,
  SprintStatus,
  StoryStatus,
} from '@prisma/client'

export type EligibilityReason = 'DONE' | 'IN_OTHER_SPRINT'

export type CrossSprintBlock = {
  storyId: string
  sprintId: string
  sprintName: string
}

export type EligibilityPartition = {
  eligible: string[]
  notEligible: { storyId: string; reason: EligibilityReason }[]
  crossSprint: CrossSprintBlock[]
}

type StoryEligibilityInput = {
  sprint_id: string | null
  status: StoryStatus
  // De bijbehorende sprint, indien sprint_id !== null. Alleen sprints met
  // status='OPEN' blokkeren eligibility — een story uit een CLOSED/ARCHIVED/
  // FAILED sprint is weer beschikbaar voor planning (consistent met
  // getBlockingSprintMap, dat alleen OPEN sprints als blocking telt).
  sprint?: { status: SprintStatus } | null
}

export function isEligibleForSprint(story: StoryEligibilityInput): boolean {
  if (story.status === 'DONE') return false
  if (story.sprint_id === null) return true
  // Story heeft een sprint_id — sprint-status MOET bekend zijn om eligibility
  // te bepalen. Ontbrekende sprint-data is conservatief: niet eligible.
  if (!story.sprint) return false
  return story.sprint.status !== 'OPEN'
}

type PrismaLike = Pick<PrismaClient, 'story'> | Prisma.TransactionClient

export async function partitionByEligibility(
  prisma: PrismaLike,
  storyIds: string[],
  excludeSprintId?: string,
): Promise<EligibilityPartition> {
  if (storyIds.length === 0) {
    return { eligible: [], notEligible: [], crossSprint: [] }
  }

  const stories = await prisma.story.findMany({
    where: { id: { in: storyIds } },
    select: {
      id: true,
      sprint_id: true,
      status: true,
      sprint: { select: { id: true, code: true, status: true } },
    },
  })

  const eligible: string[] = []
  const notEligible: { storyId: string; reason: EligibilityReason }[] = []
  const crossSprint: CrossSprintBlock[] = []

  for (const story of stories) {
    // Een story blokkeert alleen als hij in een ANDERE sprint zit DIE NOG OPEN
    // is. Stories die in een CLOSED/ARCHIVED/FAILED sprint zaten, zijn weer
    // vrij voor planning — consistent met getBlockingSprintMap.
    const inOtherActiveSprint =
      story.sprint_id !== null &&
      story.sprint_id !== excludeSprintId &&
      story.sprint?.status === 'OPEN'

    if (inOtherActiveSprint && story.sprint) {
      crossSprint.push({
        storyId: story.id,
        sprintId: story.sprint.id,
        sprintName: story.sprint.code,
      })
      notEligible.push({ storyId: story.id, reason: 'IN_OTHER_SPRINT' })
      continue
    }

    if (story.status === 'DONE') {
      notEligible.push({ storyId: story.id, reason: 'DONE' })
      continue
    }

    eligible.push(story.id)
  }

  return { eligible, notEligible, crossSprint }
}

export async function getBlockingSprintMap(
  prisma: PrismaLike,
  productId: string,
  storyIds: string[],
  excludeSprintId?: string,
): Promise<Map<string, { sprintId: string; sprintName: string }>> {
  const out = new Map<string, { sprintId: string; sprintName: string }>()
  if (storyIds.length === 0) return out

  const stories = await prisma.story.findMany({
    where: {
      id: { in: storyIds },
      product_id: productId,
      sprint_id: { not: null },
      sprint: { status: 'OPEN' },
    },
    select: {
      id: true,
      sprint_id: true,
      sprint: { select: { id: true, code: true, status: true } },
    },
  })

  for (const story of stories) {
    if (!story.sprint) continue
    if (excludeSprintId !== undefined && story.sprint.id === excludeSprintId) continue
    out.set(story.id, {
      sprintId: story.sprint.id,
      sprintName: story.sprint.code,
    })
  }

  return out
}
