/**
 * The gitea label schema — CommiTea's only footprint in managed work repos.
 * Fixed sets by design (docs/PLAN.md): estimates are coarse on purpose, the
 * scheduler's calibration layer refines them; anything outside these sets is
 * someone else's label and none of our business.
 */

export const ESTIMATE_LABELS = ['est/1d', 'est/2d', 'est/3d', 'est/5d', 'est/8d'] as const
export const PRIORITY_LABELS = ['p/1', 'p/2', 'p/3', 'p/4'] as const
export const HARD_DEADLINE_LABEL = 'deadline/hard'

export type EstimateLabel = (typeof ESTIMATE_LABELS)[number]
export type PriorityLabel = (typeof PRIORITY_LABELS)[number]

const COMMITEA_NAMESPACES = ['est/', 'p/', 'deadline/'] as const

export interface LabelFacts {
  /** Days from an `est/*` label; null when absent. Largest wins on conflict. */
  estimateDays: number | null
  /** 1 (most urgent) … 4 from a `p/*` label; null when absent. Most urgent wins on conflict. */
  priority: number | null
  hardDeadline: boolean
  /** Labels inside CommiTea namespaces that don't match the fixed sets. */
  malformed: string[]
  /** Valid CommiTea labels that duplicated an axis (e.g. two `est/*` labels). */
  conflicts: string[]
}

export function parseEstimateLabel(label: string): number | null {
  if (!(ESTIMATE_LABELS as readonly string[]).includes(label)) return null
  return Number(/^est\/(\d+)d$/.exec(label)![1])
}

export function parsePriorityLabel(label: string): number | null {
  if (!(PRIORITY_LABELS as readonly string[]).includes(label)) return null
  return Number(label.slice(2))
}

export function isCommiteaLabel(label: string): boolean {
  return COMMITEA_NAMESPACES.some((ns) => label.startsWith(ns))
}

/**
 * Reduce an issue's label list to scheduler inputs. Conflicting labels on one
 * axis resolve pessimistically (largest estimate) / urgently (lowest priority
 * number) and are reported in `conflicts` so the agent can nag about them.
 */
export function extractLabelFacts(labels: readonly string[]): LabelFacts {
  const facts: LabelFacts = {
    estimateDays: null,
    priority: null,
    hardDeadline: false,
    malformed: [],
    conflicts: [],
  }

  for (const label of labels) {
    if (!isCommiteaLabel(label)) continue

    if (label === HARD_DEADLINE_LABEL) {
      facts.hardDeadline = true
      continue
    }

    const estimate = parseEstimateLabel(label)
    if (estimate !== null) {
      if (facts.estimateDays !== null) {
        facts.conflicts.push(label)
        facts.estimateDays = Math.max(facts.estimateDays, estimate)
      } else {
        facts.estimateDays = estimate
      }
      continue
    }

    const priority = parsePriorityLabel(label)
    if (priority !== null) {
      if (facts.priority !== null) {
        facts.conflicts.push(label)
        facts.priority = Math.min(facts.priority, priority)
      } else {
        facts.priority = priority
      }
      continue
    }

    facts.malformed.push(label)
  }

  return facts
}
