import type { RequestRow } from './request-types'

/** Astryx `Badge` variant chosen per priority label. Purely visual — never changes the priority text itself. */
export type PriorityVariant = 'error' | 'warning' | 'neutral' | 'info'

export function priorityVariant(priority: string): PriorityVariant {
  const normalized = priority.trim().toUpperCase()
  if (normalized === 'FIRE') return 'error'
  if (normalized === 'HIGH') return 'warning'
  if (normalized === 'NORMAL') return 'neutral'
  return 'info'
}

/** Ascending by `asked_at`, tiebroken by `id` so ties (same seed date) get a stable, non-reshuffling order. */
export function sortByAskedOrder(rows: readonly RequestRow[]): RequestRow[] {
  return [...rows].sort((a, b) => {
    const byAsked = Date.parse(a.asked_at) - Date.parse(b.asked_at)
    return byAsked !== 0 ? byAsked : a.id.localeCompare(b.id)
  })
}

export function ordinal(n: number): string {
  const mod100 = n % 100
  if (mod100 >= 11 && mod100 <= 13) return `${n}th`
  switch (n % 10) {
    case 1: return `${n}st`
    case 2: return `${n}nd`
    case 3: return `${n}rd`
    default: return `${n}th`
  }
}

/** `index` is zero-based position within the asked-ordered queue. */
export function queuePositionLabel(index: number): string {
  return `${ordinal(index + 1)} in line`
}

/**
 * Honest-generic only: the store has no lane-capacity or land-queue-depth field, so this
 * derives strictly from queue position and never distinguishes "free lane" from "land queue".
 */
export function waitReason(index: number): string {
  if (index === 0) return 'Next up once a lane opens'
  return `${index} request${index === 1 ? '' : 's'} ahead in line`
}

export const STAGE_LABELS = ['spec', 'build', 'verify', 'land', 'deploy', 'live'] as const
export type StageLabel = (typeof STAGE_LABELS)[number]

/**
 * The store carries no per-request stage/phase column, so the current stage is always
 * unknown — this always returns null rather than guessing from state/worker/detail.
 */
export function stageActiveStep(_row: RequestRow): number | null {
  return null
}

export type ShippedBucket = 'Today' | 'Yesterday' | 'This week' | 'Earlier'

function startOfDay(date: Date): Date {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate())
}

/** Buckets by `updated_at` — the store has no `shipped_at` column, so this is the honest proxy. */
export function shippedBucket(updatedAtIso: string, now: Date): ShippedBucket {
  const diffDays = Math.round((startOfDay(now).getTime() - startOfDay(new Date(updatedAtIso)).getTime()) / 86_400_000)
  if (diffDays <= 0) return 'Today'
  if (diffDays === 1) return 'Yesterday'
  if (diffDays <= 7) return 'This week'
  return 'Earlier'
}

export const SHIPPED_BUCKET_ORDER: readonly ShippedBucket[] = ['Today', 'Yesterday', 'This week', 'Earlier']

export function groupShipped(rows: readonly RequestRow[], now: Date): Record<ShippedBucket, RequestRow[]> {
  const groups: Record<ShippedBucket, RequestRow[]> = { Today: [], Yesterday: [], 'This week': [], Earlier: [] }
  for (const row of rows) groups[shippedBucket(row.updated_at, now)].push(row)
  for (const bucket of SHIPPED_BUCKET_ORDER) groups[bucket].sort((a, b) => Date.parse(b.updated_at) - Date.parse(a.updated_at))
  return groups
}

export interface BoardEventView {
  id: string
  at: string
  label: string
  kind: string
}

export interface DeliveryEventView extends BoardEventView {
  requestId: string
  requestTitle: string
  project: string
}

export function latestRequestEvent(row: RequestRow): BoardEventView | null {
  const receipts = row.receipt_trail ?? []
  const newestReceipt = receipts.reduce<(typeof receipts)[number] | null>((latest, receipt) => {
    if (!latest) return receipt
    return Date.parse(receipt.at) > Date.parse(latest.at) ? receipt : latest
  }, null)
  const transitions = row.transition_trail ?? []
  const newestTransition = transitions.reduce<(typeof transitions)[number] | null>((latest, transition) => {
    if (!latest) return transition
    return Date.parse(transition.at) > Date.parse(latest.at) ? transition : latest
  }, null)
  if (newestTransition && (!newestReceipt || Date.parse(newestTransition.at) > Date.parse(newestReceipt.at))) {
    return {
      id: newestTransition.id,
      at: newestTransition.at,
      label: newestTransition.reason ?? `Moved to ${newestTransition.to_state === 'blocked_needs_owner' ? 'blocked — needs you' : newestTransition.to_state.replace('_', ' ')}`,
      kind: 'transition',
    }
  }
  if (newestReceipt) {
    return { id: newestReceipt.id, at: newestReceipt.at, label: newestReceipt.line, kind: newestReceipt.kind }
  }

  if (row.detail) return { id: `${row.id}:detail`, at: row.updated_at, label: row.detail, kind: 'detail' }
  return null
}

export function deliveryEvents(rows: readonly RequestRow[]): DeliveryEventView[] {
  return rows.flatMap((row) => (row.receipt_trail ?? [])
    .filter((receipt) => receipt.kind === 'landed' || receipt.kind === 'deployed')
    .map((receipt) => ({
      id: receipt.id,
      requestId: row.id,
      requestTitle: row.title,
      project: row.project,
      at: receipt.at,
      label: receipt.line,
      kind: receipt.kind,
    })))
    .sort((a, b) => Date.parse(b.at) - Date.parse(a.at))
}

export interface BoardSummary {
  inFlight: number
  queued: number
  blocked: number
  needsRecovery: number
  shippedToday: number
}

export function summaryCounts(rows: readonly RequestRow[], now: Date): BoardSummary {
  return {
    inFlight: rows.filter((row) => row.state === 'in_flight').length,
    queued: rows.filter((row) => row.state === 'asked').length,
    blocked: rows.filter((row) => row.state === 'blocked_needs_owner').length,
    needsRecovery: rows.filter((row) => row.state === 'orphaned').length,
    shippedToday: rows.filter((row) => row.state === 'shipped' && shippedBucket(row.updated_at, now) === 'Today').length,
  }
}

export function distinctProjects(rows: readonly RequestRow[]): string[] {
  return Array.from(new Set(rows.map((row) => row.project))).sort((a, b) => a.localeCompare(b))
}

export function matchesSearch(row: RequestRow, query: string): boolean {
  const q = query.trim().toLowerCase()
  if (!q) return true
  return [row.title, row.project, row.detail ?? ''].some((field) => field.toLowerCase().includes(q))
}

export function matchesProject(row: RequestRow, project: string | null): boolean {
  return project === null || row.project === project
}

export function distinctPlanSlugs(rows: readonly RequestRow[]): string[] {
  return Array.from(new Set(rows.flatMap((row) => row.plan_ref ? [row.plan_ref] : []))).sort((a, b) => a.localeCompare(b))
}

/** The owner sees a friendly Session name when available, otherwise a compact session ID. */
export function sessionFilterValue(row: RequestRow): string | null {
  return row.session_name ?? row.session_id ?? null
}

export function sessionFilterLabel(value: string, isSessionId = true): string {
  return isSessionId && value.length > 12 ? `${value.slice(0, 8)}…${value.slice(-4)}` : value
}

export function distinctSessions(rows: readonly RequestRow[]): string[] {
  return Array.from(new Set(rows.flatMap((row) => {
    const value = sessionFilterValue(row)
    return value ? [value] : []
  }))).sort((a, b) => a.localeCompare(b))
}

export function matchesPlanSlug(row: RequestRow, planSlug: string | null): boolean {
  return planSlug === null || row.plan_ref === planSlug
}

export function matchesSession(row: RequestRow, session: string | null): boolean {
  return session === null || sessionFilterValue(row) === session
}
