import { LiveVideoStartPayload } from '@peertube/peertube-models'
import { logger, loggerTagsFactory } from '@server/helpers/logger.js'
import { startScheduledLiveSession } from '@server/controllers/api/live-stream.js'
import { CONFIG } from '@server/initializers/config.js'
import { LiveVideoProcessManager } from '@server/lib/live-videos/live-video-process-manager.js'
import { LIVE_VIDEO_START_LEAD_TIME_MS } from '@server/lib/live-videos/shared.js'
import { LiveVideoModel, LiveVideoStatus } from '@server/models/video/live-video.js'
import { Job } from 'bullmq'
import { setTimeout as wait } from 'node:timers/promises'

const lTags = loggerTagsFactory('jobs', 'live-video-start')
const LIVE_VIDEO_START_POLL_MS = 5_000

export async function processLiveVideoStart (job: Job) {
  const payload = job.data as LiveVideoStartPayload
  let liveVideo: LiveVideoModel

  while (true) {
    const currentLiveVideo = await LiveVideoModel.loadById(payload.liveVideoId)
    if (!currentLiveVideo) {
      logger.warn('Cannot find live video %d for scheduled start job %s.', payload.liveVideoId, job.id, lTags())
      return
    }

    if (currentLiveVideo.status !== LiveVideoStatus.SCHEDULED) {
      logger.info('Skipping scheduled start of live video %d because status is %s.', currentLiveVideo.id, currentLiveVideo.status, lTags())
      return
    }

    const now = Date.now()
    const startAt = currentLiveVideo.startTime.getTime()
    const endAt = currentLiveVideo.endTime.getTime()
    if (endAt <= now) {
      logger.info('Skipping scheduled start of live video %d because it is already finished.', currentLiveVideo.id, lTags())
      return
    }

    const launchAt = startAt - LIVE_VIDEO_START_LEAD_TIME_MS
    const waitMs = launchAt - now
    if (waitMs <= 0) {
      liveVideo = currentLiveVideo
      break
    }

    await wait(Math.min(waitMs, LIVE_VIDEO_START_POLL_MS))
  }

  if (CONFIG.LIVE.ENABLE_SOURCE_START) {
    await LiveVideoProcessManager.Instance.start(liveVideo)
  } else {
    logger.info('Skipping source-side scheduled live start for live video %d because live.enable_source_start is disabled.', liveVideo.id, lTags())
  }

  const sinkWaitMs = liveVideo.startTime.getTime() - Date.now()
  if (sinkWaitMs > 0) {
    await wait(sinkWaitMs)
  }

  if (CONFIG.LIVE.ENABLE_SINK_START) {
    await startScheduledLiveSession(liveVideo.targetCamera, { keepAliveUntil: liveVideo.endTime })
  } else {
    logger.info('Skipping sink-side scheduled live start for live video %d because live.enable_sink_start is disabled.', liveVideo.id, lTags())
  }
}
