import type { ProductWorkspaceStore } from './store'
import type {
  BacklogPbi,
  BacklogStory,
  BacklogTask,
  CrossSprintBlock,
  TaskDetail,
} from './types'

export type PbiTriState = 'empty' | 'partial' | 'full'

// G1: stable EMPTY-references zodat selectors geen nieuwe array per call retourneren.
const EMPTY_PBIS: BacklogPbi[] = []
const EMPTY_STORIES: BacklogStory[] = []
const EMPTY_TASKS: (BacklogTask | TaskDetail)[] = []

/**
 * Lijst-selector. Vereist `useShallow` in componenten (G2) — anders re-rendert
 * elke ongerelateerde store-mutatie het component.
 */
export function selectVisiblePbis(s: ProductWorkspaceStore): BacklogPbi[] {
  if (s.relations.pbiIds.length === 0) return EMPTY_PBIS
  const out: BacklogPbi[] = []
  for (const id of s.relations.pbiIds) {
    const pbi = s.entities.pbisById[id]
    if (pbi) out.push(pbi)
  }
  return out.length === 0 ? EMPTY_PBIS : out
}

/**
 * Lijst-selector. Vereist `useShallow` in componenten (G2).
 */
export function selectStoriesForActivePbi(s: ProductWorkspaceStore): BacklogStory[] {
  const pbiId = s.context.activePbiId
  if (!pbiId) return EMPTY_STORIES
  const ids = s.relations.storyIdsByPbi[pbiId]
  if (!ids || ids.length === 0) return EMPTY_STORIES
  const out: BacklogStory[] = []
  for (const id of ids) {
    const story = s.entities.storiesById[id]
    if (story) out.push(story)
  }
  return out.length === 0 ? EMPTY_STORIES : out
}

/**
 * Lijst-selector. Vereist `useShallow` in componenten (G2).
 */
export function selectTasksForActiveStory(
  s: ProductWorkspaceStore,
): (BacklogTask | TaskDetail)[] {
  const storyId = s.context.activeStoryId
  if (!storyId) return EMPTY_TASKS
  const ids = s.relations.taskIdsByStory[storyId]
  if (!ids || ids.length === 0) return EMPTY_TASKS
  const out: (BacklogTask | TaskDetail)[] = []
  for (const id of ids) {
    const task = s.entities.tasksById[id]
    if (task) out.push(task)
  }
  return out.length === 0 ? EMPTY_TASKS : out
}

/**
 * Single-value selector. `useShallow` niet vereist — retourneert stable
 * entity-reference (zelfde object zolang entity ongewijzigd).
 */
export function selectActivePbi(s: ProductWorkspaceStore): BacklogPbi | null {
  const id = s.context.activePbiId
  if (!id) return null
  return s.entities.pbisById[id] ?? null
}

/**
 * Single-value selector. `useShallow` niet vereist.
 */
export function selectActiveStory(s: ProductWorkspaceStore): BacklogStory | null {
  const id = s.context.activeStoryId
  if (!id) return null
  return s.entities.storiesById[id] ?? null
}

/**
 * Single-value selector. `useShallow` niet vereist.
 */
export function selectActiveTask(
  s: ProductWorkspaceStore,
): BacklogTask | TaskDetail | null {
  const id = s.context.activeTaskId
  if (!id) return null
  return s.entities.tasksById[id] ?? null
}

/**
 * Single-value selector voor stories binnen een specifiek PBI (niet per se actief).
 */
export function selectStoriesForPbi(
  s: ProductWorkspaceStore,
  pbiId: string,
): BacklogStory[] {
  const ids = s.relations.storyIdsByPbi[pbiId]
  if (!ids || ids.length === 0) return EMPTY_STORIES
  const out: BacklogStory[] = []
  for (const id of ids) {
    const story = s.entities.storiesById[id]
    if (story) out.push(story)
  }
  return out.length === 0 ? EMPTY_STORIES : out
}

// PBI-79 / ST-1336 — sprint-membership selectors.
//
// Tri-state PBI-vinkje. Werkt op counts uit het summary-endpoint zolang
// de PBI dichtgeklapt is (relations.storyIdsByPbi leeg). Wanneer stories
// geladen zijn rekenen we ook de pending-buffer mee per-story.
export function selectPbiTriState(
  s: ProductWorkspaceStore,
  pbiId: string,
): PbiTriState {
  const summary = s.sprintMembership.pbiSummary[pbiId]
  if (!summary || summary.totalStoryCount === 0) return 'empty'

  const storyIds = s.relations.storyIdsByPbi[pbiId]
  let inSprintAfterPending = summary.inActiveSprintStoryCount

  if (storyIds && storyIds.length > 0) {
    const idSet = new Set(storyIds)
    const adds = s.sprintMembership.pending.adds
    const removes = s.sprintMembership.pending.removes
    for (const id of adds) if (idSet.has(id)) inSprintAfterPending++
    for (const id of removes) if (idSet.has(id)) inSprintAfterPending--
  }

  if (inSprintAfterPending <= 0) return 'empty'
  if (inSprintAfterPending >= summary.totalStoryCount) return 'full'
  return 'partial'
}

/**
 * Effectief membership van een story rekening houdend met de pending buffer.
 * `activeSprintId` is de gekozen sprint (state B); zonder die context valt de
 * selector terug op de DB-waarde.
 */
export function selectStoryEffectiveInSprint(
  s: ProductWorkspaceStore,
  storyId: string,
  activeSprintId: string | null,
): boolean {
  const story = s.entities.storiesById[storyId]
  const inSprintDb = story?.sprint_id === activeSprintId && activeSprintId !== null
  const inAdds = s.sprintMembership.pending.adds.includes(storyId)
  const inRemoves = s.sprintMembership.pending.removes.includes(storyId)
  if (inAdds) return true
  if (inRemoves) return false
  return inSprintDb
}

export function selectStoryIsBlocked(
  s: ProductWorkspaceStore,
  storyId: string,
): CrossSprintBlock | null {
  return s.sprintMembership.crossSprintBlocks[storyId] ?? null
}

export function selectIsDirty(s: ProductWorkspaceStore): boolean {
  return (
    s.sprintMembership.pending.adds.length +
      s.sprintMembership.pending.removes.length >
    0
  )
}

export function selectPendingCount(s: ProductWorkspaceStore): number {
  return (
    s.sprintMembership.pending.adds.length +
    s.sprintMembership.pending.removes.length
  )
}
