import { useEffect, useRef, type MutableRefObject } from 'react'
import {
  reportPlaybackStart,
  reportPlaybackProgress,
  reportPlaybackStop,
} from './use-playback-report'
import { debugLog } from '../lib/log'

type PlayMethod = 'DirectPlay' | 'DirectStream' | 'Transcode'

interface Args {
  itemId: string | undefined
  mediaSourceId: string | undefined
  startTimeTicks: number | undefined
  playSessionId: string | undefined
  playMethod: PlayMethod | undefined
  audioIndex: number | null
  subtitleIndex: number | null
  isPaused: boolean
  isMuted: boolean
  volume: number
  /**
   * Pinned ref to the latest playback position in ticks. The hook reads
   * `.current` on every progress / stop tick so the caller only needs to
   * keep it up-to-date - no deps churn.
   */
  progressRef: MutableRefObject<number>
}

/** Defensive tick-to-number conversion - returns 0 for NaN / non-finite
 *  values so the report payload is always serialisable. */
function ticksToNum(n: number): number {
  if (!Number.isFinite(n)) return 0
  return Math.max(0, Math.floor(n))
}

/**
 * Wires the Jellyfin Start / Progress / Stop reporting cycle for the
 * current playback. Mirrors what the official jellyfin-web client sends:
 *  - PlaySessionId so the server can clean up an orphaned transcode if
 *    the user closes mid-playback
 *  - PlayMethod so the Active Devices dashboard shows the right mode
 *  - audio/subtitle index, pause, mute, and volume kept in sync
 *
 * Progress reports fire on a 5s interval - more granular than the
 * previous 10s, not as chatty as jellyfin-web's per-timeupdate.
 *
 * The hook intentionally only re-runs on itemId. Session state changes
 * (mute, pause, audio index) flow through a pinned ref so the interval
 * picks them up without re-creating the reporting session.
 */
export function usePlaybackReporting(args: Args) {
  const {
    itemId,
    mediaSourceId,
    startTimeTicks,
    playSessionId,
    playMethod,
    audioIndex,
    subtitleIndex,
    isPaused,
    isMuted,
    volume,
    progressRef,
  } = args

  const sessionRef = useRef<{
    playSessionId?: string
    playMethod?: PlayMethod
    audioIndex: number | null
    subtitleIndex: number | null
    isPaused: boolean
    isMuted: boolean
    volume: number
  }>({
    playSessionId,
    playMethod,
    audioIndex,
    subtitleIndex,
    isPaused,
    isMuted,
    volume,
  })
  // Pin the latest values without re-binding the interval. A deps change
  // here would tear down + re-create the session, sending duplicate Start
  // and Stop reports for the same play.
  sessionRef.current = {
    playSessionId,
    playMethod,
    audioIndex,
    subtitleIndex,
    isPaused,
    isMuted,
    volume,
  }

  useEffect(() => {
    if (!itemId) return
    const safeStart = startTimeTicks && Number.isFinite(startTimeTicks)
      ? Math.floor(startTimeTicks)
      : undefined

    function buildContext(positionTicks: number) {
      return {
        itemId: itemId || '',
        mediaSourceId,
        positionTicks,
        playSessionId: sessionRef.current.playSessionId,
        playMethod: sessionRef.current.playMethod,
        audioStreamIndex: sessionRef.current.audioIndex ?? undefined,
        subtitleStreamIndex: sessionRef.current.subtitleIndex ?? undefined,
        isPaused: sessionRef.current.isPaused,
        isMuted: sessionRef.current.isMuted,
        volumeLevel: Math.round((sessionRef.current.volume ?? 1) * 100),
      }
    }

    reportPlaybackStart({ ...buildContext(safeStart ?? 0), positionTicks: safeStart }).catch(err =>
      debugLog('[playback] start report failed', err),
    )

    const id = setInterval(() => {
      reportPlaybackProgress(buildContext(ticksToNum(progressRef.current))).catch(err =>
        debugLog('[playback] progress report failed', err),
      )
    }, 5000)

    const currentPosition = () => ticksToNum(progressRef.current)

    return () => {
      clearInterval(id)
      reportPlaybackStop(buildContext(currentPosition())).catch(err =>
        debugLog('[playback] stop report failed', err),
      )
    }
  }, [itemId, mediaSourceId, startTimeTicks, progressRef])
}
