// Builds per-day schedule structures (sessions, breaks, slide ranges) from parsed events.
import * as sort from './sort.js'

export function slideRangesFromSentinels(text, count) {
  const starts = Array.from({ length: count })
  const ends = Array.from({ length: count })
  let slide = 1
  for (const line of text.split('\n')) {
    if (line === '---') { slide++; continue }
    const startMatch = line.match(/^<!-- __item_(\d+)_start -->$/)
    if (startMatch) { starts[Number(startMatch[1])] = slide; continue }
    const endMatch = line.match(/^<!-- __item_(\d+)_end -->$/)
    if (endMatch) { ends[Number(endMatch[1])] = slide }
  }
  return starts.map((start, i) => [start, ends[i]])
}

function buildBreakEntry(event, slideRanges, impliedBreaks, dayIdx, breakIdx) {
  const implied = impliedBreaks[dayIdx]?.[breakIdx] ?? null
  if (implied != null && event.duration != null && event.duration !== implied) {
    throw new Error(`break '${event.type}' on day ${dayIdx + 1} declares ${event.duration}m but timings imply ${implied}m`)
  }
  const effectiveDuration = implied ?? event.duration ?? null
  const entry = { type: 'break', slug: event.type }
  if (effectiveDuration != null) entry.duration = effectiveDuration
  if (event.itemIdx != null && slideRanges[event.itemIdx]) entry.slide_range = slideRanges[event.itemIdx]
  return sort.byKeys(entry)
}

function buildDayEntry(daySchedule, startTimes, dayIdx) {
  const startTime = startTimes[dayIdx] ?? null
  const dayEntry = { schedule: daySchedule }
  if (startTime != null) dayEntry.start_time = startTime
  return sort.byKeys(dayEntry)
}

function buildContentItem(event, slideRanges) {
  return sort.byKeys({
    ...(event.duration != null ? { duration: event.duration } : {}),
    slide_range: slideRanges[event.itemIdx],
    slug: event.slug,
    title: event.title,
    type: event.type,
    ...event.kv,
  })
}

export function buildDays(allEvents, slideRanges, sessionDurationsByDay, startTimes = [], impliedBreaks = []) {
  const days = []
  const sessionCountsPerDay = []
  let daySchedule = []
  let sessionItems = []
  let dayIdx = 0
  let sessionIdx = 0
  let breakIdx = 0

  function closeSession() {
    if (sessionItems.length === 0) return
    const firstSlide = sessionItems[0]?.slide_range?.[0] ?? 1
    const lastSlide = sessionItems.at(-1)?.slide_range?.[1] ?? firstSlide
    const duration = sessionDurationsByDay[dayIdx]?.[sessionIdx] ?? 90
    const contentSum = sessionItems.reduce((sum, item) => sum + (item.duration ?? 0), 0)
    if (contentSum > duration) {
      throw new Error(`session ${sessionIdx + 1} on day ${dayIdx + 1} has ${contentSum}m of content but only ${duration}m declared`)
    }
    daySchedule.push(sort.byKeys({ duration, items: sessionItems, type: 'session', slide_range: [firstSlide, lastSlide], slug: `session-${sessionIdx + 1}` }))
    sessionIdx++
    sessionItems = []
  }

  for (const event of allEvents) {
    if (event.kind === 'break') {
      if (event.type === 'end') {
        closeSession()
        if (sessionIdx === 0 && daySchedule.length === 0) {
          process.stdout.write('  [warning] consecutive *** produced an empty day — remove the extra ***\n')
          continue
        }
        sessionCountsPerDay.push(sessionIdx)
        days.push(buildDayEntry(daySchedule, startTimes, dayIdx))
        daySchedule = []; sessionIdx = 0; breakIdx = 0; dayIdx++
      } else {
        closeSession()
        daySchedule.push(buildBreakEntry(event, slideRanges, impliedBreaks, dayIdx, breakIdx++))
      }
    } else {
      sessionItems.push(buildContentItem(event, slideRanges))
    }
  }

  if (sessionItems.length > 0 || daySchedule.length > 0) {
    closeSession()
    sessionCountsPerDay.push(sessionIdx)
    days.push(buildDayEntry(daySchedule, startTimes, dayIdx))
  }

  return { days, sessionCountsPerDay }
}
