/* ──────────────────────────────────────────────────────────────
 *  Jellyfin metadata derivation
 *  Pure helpers that read MediaSources / MediaStreams off a
 *  BaseItemDto and project them into display-ready shapes.
 * ──────────────────────────────────────────────────────────── */

import type { BaseItemDto } from '../api/types'
import { languageLabel } from './format'

interface MediaStreamLike {
  Type?: string
  Codec?: string | null
  Profile?: string | null
  Language?: string | null
  Title?: string | null
  Channels?: number | null
  ChannelLayout?: string | null
  SampleRate?: number | null
  BitRate?: number | null
  BitDepth?: number | null
  Width?: number | null
  Height?: number | null
  AverageFrameRate?: number | null
  RealFrameRate?: number | null
  AspectRatio?: string | null
  ColorSpace?: string | null
  ColorTransfer?: string | null
  PixelFormat?: string | null
  IsDefault?: boolean | null
  IsForced?: boolean | null
  IsHearingImpaired?: boolean | null
  IsExternal?: boolean | null
  VideoRange?: string | null
  VideoRangeType?: string | null
  AudioSpatialFormat?: string | null
  Hdr10PlusPresentFlag?: boolean | null
  DvProfile?: number | null
  DvVersionMajor?: number | null
  Index?: number
  DisplayTitle?: string | null
}

interface MediaSourceLike {
  Id?: string | null
  Name?: string | null
  Container?: string | null
  Size?: number | null
  Bitrate?: number | null
  RunTimeTicks?: number | null
  MediaStreams?: MediaStreamLike[] | null
}

/** Returns the best (default) media source from an item, falling back to first. */
export function pickPrimarySource(item: Pick<BaseItemDto, 'MediaSources'>): MediaSourceLike | null {
  const sources = (item.MediaSources || []) as MediaSourceLike[]
  if (!sources.length) return null
  return sources[0]
}

/** All media streams across primary source. */
export function getStreams(item: Pick<BaseItemDto, 'MediaSources'>): MediaStreamLike[] {
  const src = pickPrimarySource(item)
  return src?.MediaStreams || []
}

export function getVideoStream(item: Pick<BaseItemDto, 'MediaSources'>): MediaStreamLike | null {
  const streams = getStreams(item)
  return streams.find(s => s.Type === 'Video') || null
}

export function getAudioStreams(item: Pick<BaseItemDto, 'MediaSources'>): MediaStreamLike[] {
  return getStreams(item).filter(s => s.Type === 'Audio')
}

export function getSubtitleStreams(item: Pick<BaseItemDto, 'MediaSources'>): MediaStreamLike[] {
  return getStreams(item).filter(s => s.Type === 'Subtitle')
}

/** "4K" / "1080p" / "720p" / "480p" / null */
export function resolutionLabel(item: Pick<BaseItemDto, 'MediaSources' | 'Width' | 'Height'>): string | null {
  const v = getVideoStream(item)
  const w = v?.Width ?? item.Width ?? null
  const h = v?.Height ?? item.Height ?? null
  if (!w || !h) return null
  if (w >= 3800 || h >= 2100) return '4K'
  if (w >= 2500 || h >= 1400) return '2K'
  if (h >= 1000) return '1080p'
  if (h >= 700) return '720p'
  if (h >= 400) return '480p'
  return 'SD'
}

/** "Dolby Vision" / "HDR10+" / "HDR10" / "HDR" / null (omit "SDR") */
export function videoRangeLabel(item: Pick<BaseItemDto, 'MediaSources'>): string | null {
  const v = getVideoStream(item)
  if (!v) return null
  if (v.VideoRangeType === 'DOVI' || (v.DvProfile != null && v.DvProfile > 0)) return 'Dolby Vision'
  if (v.Hdr10PlusPresentFlag) return 'HDR10+'
  if (v.VideoRangeType === 'HDR10') return 'HDR10'
  if ((v.VideoRange || '').toUpperCase() === 'HDR') return 'HDR'
  return null
}

/** Compact spatial/channel label: "Atmos" | "DTS:X" | "7.1" | "5.1" | "Stereo" | null */
export function audioBadgeLabel(stream: MediaStreamLike | null): string | null {
  if (!stream) return null
  const spatial = (stream.AudioSpatialFormat || '').toLowerCase()
  if (spatial === 'dolbyatmos') return 'Atmos'
  if (spatial === 'dtsx') return 'DTS:X'
  const layout = (stream.ChannelLayout || '').toLowerCase()
  const ch = stream.Channels || 0
  if (layout.includes('7.1') || ch >= 8) return '7.1'
  if (layout.includes('5.1') || ch === 6) return '5.1'
  if (ch === 2) return 'Stereo'
  if (ch === 1) return 'Mono'
  return null
}

/** Verbose audio descriptor: "Dolby Atmos" / "DTS-HD MA 7.1" / "AC3 5.1" / "AAC 2.0" */
export function audioFormatLabel(stream: MediaStreamLike | null): string | null {
  if (!stream) return null
  const codec = (stream.Codec || '').toLowerCase()
  const profile = (stream.Profile || '').toLowerCase()
  const ch = stream.Channels || 0
  const layoutMap: Record<number, string> = { 1: 'Mono', 2: 'Stereo', 6: '5.1', 8: '7.1' }
  const channels = layoutMap[ch] || (ch ? `${ch}ch` : '')

  let codecName = codec.toUpperCase()
  if (codec === 'eac3') codecName = profile.includes('atmos') ? 'Dolby Atmos' : 'EAC3'
  else if (codec === 'truehd') codecName = profile.includes('atmos') ? 'Dolby TrueHD Atmos' : 'TrueHD'
  else if (codec === 'dts') codecName = profile.includes('hd ma') ? 'DTS-HD MA' : profile.includes('hd-hra') ? 'DTS-HD HRA' : profile.includes(':x') ? 'DTS:X' : 'DTS'
  else if (codec === 'ac3') codecName = 'Dolby Digital'
  else if (codec === 'opus') codecName = 'Opus'
  else if (codec === 'flac') codecName = 'FLAC'
  else if (codec === 'aac') codecName = 'AAC'

  return [codecName, channels].filter(Boolean).join(' ')
}

/** Subtitle description: "English (Forced)" / "Spanish (SDH)" / "Japanese" */
export function subtitleLabel(stream: MediaStreamLike): string {
  const lang = languageLabel(stream.Language)
  const flags: string[] = []
  if (stream.IsForced) flags.push('Forced')
  if (stream.IsHearingImpaired) flags.push('SDH')
  if (stream.IsExternal) flags.push('External')
  if (flags.length) return `${lang || 'Unknown'} (${flags.join(', ')})`
  return lang || stream.Title || 'Unknown'
}

/** "H.264 High" or "HEVC Main 10" or "AV1" */
export function videoCodecLabel(stream: MediaStreamLike | null): string {
  if (!stream) return ''
  const codec = (stream.Codec || '').toLowerCase()
  const profile = stream.Profile || ''
  const codecName =
    codec === 'h264' ? 'H.264'
    : codec === 'hevc' ? 'HEVC'
    : codec === 'av1' ? 'AV1'
    : codec === 'vp9' ? 'VP9'
    : codec === 'mpeg2video' ? 'MPEG-2'
    : codec.toUpperCase()
  return [codecName, profile].filter(Boolean).join(' ')
}

/** "23.976 fps" */
export function framerateLabel(stream: MediaStreamLike | null): string {
  if (!stream) return ''
  const fps = stream.AverageFrameRate || stream.RealFrameRate
  if (!fps) return ''
  return `${(Math.round(fps * 1000) / 1000).toString().replace(/\.?0+$/, '')} fps`
}

/** Top-line badges, capped to keep UI uncluttered. Returns 0-3 entries. */
export function getTopBadges(item: Pick<BaseItemDto, 'MediaSources' | 'Width' | 'Height'>): string[] {
  const out: string[] = []
  const res = resolutionLabel(item)
  const range = videoRangeLabel(item)
  const audio = audioBadgeLabel(getAudioStreams(item)[0] || null)

  if (res === '4K') out.push('4K')
  if (range) out.push(range)
  if (audio === 'Atmos' || audio === 'DTS:X') out.push(audio)
  return out.slice(0, 3)
}

/** Full ordered badge list for hero/detail use. */
export function getAllBadges(item: Pick<BaseItemDto, 'MediaSources' | 'Width' | 'Height'>): string[] {
  const out: string[] = []
  const res = resolutionLabel(item)
  const range = videoRangeLabel(item)
  const audio = audioBadgeLabel(getAudioStreams(item)[0] || null)
  if (res) out.push(res)
  if (range) out.push(range)
  if (audio) out.push(audio)
  return out
}

/** A MediaSource summarized for the version selector. */
export function summarizeSource(source: MediaSourceLike): {
  label: string
  detail: string
} {
  const v = (source.MediaStreams || []).find(s => s.Type === 'Video')
  const w = v?.Width || 0
  const h = v?.Height || 0
  const range =
    v?.VideoRangeType === 'DOVI' || (v?.DvProfile && v.DvProfile > 0) ? 'DV'
    : v?.Hdr10PlusPresentFlag ? 'HDR10+'
    : v?.VideoRangeType === 'HDR10' ? 'HDR'
    : (v?.VideoRange || '').toUpperCase() === 'HDR' ? 'HDR'
    : ''
  const resolution =
    w >= 3800 || h >= 2100 ? '4K'
    : h >= 1000 ? '1080p'
    : h >= 700 ? '720p'
    : h >= 400 ? '480p'
    : 'SD'
  const label = [resolution, range].filter(Boolean).join(' ')
  const detail = [
    source.Container?.toUpperCase(),
    source.Size ? `${Math.round((source.Size / 1024 / 1024 / 1024) * 10) / 10} GB` : '',
  ].filter(Boolean).join(' · ')
  return { label, detail }
}
