/**
 * Pure helpers for the Downloads view (Phase M): a one-line aggregate of the live
 * queue, and same-video detection for the duplicate guard. Kept free of any store
 * / window dependency (only a type-only import of DownloadItem) so it can be
 * unit-tested without constructing the Zustand store or its preview ticker.
 */
import type { DownloadItem } from '../store/downloads'
import { youtubeId } from '../lib/urlHelpers'
import { fmtSpeed, fmtEta } from '@shared/format'

export interface QueueSummary {
  downloading: number
  queued: number
  failed: number
  /** true when something is downloading or waiting */
  active: boolean
  /** 0..1, averaged over downloading + queued items */
  progress: number
  /** combined download speed, formatted; '' when nothing is downloading */
  speedLabel: string
  /** rough time remaining (the longest active ETA), formatted; '' when unknown */
  etaLabel: string
}

/**
 * Aggregate the live queue into a one-line summary for the Downloads header.
 * Combined speed is summed across downloading items; the "time left" is the
 * longest single ETA — a reasonable proxy for when the current batch finishes
 * (we don't track remaining bytes, so a true combined ETA isn't available).
 * Queued items count toward the progress average as 0%, so the bar reflects
 * "how much of the visible work is done", not just the active downloads.
 */
export function summarizeQueue(items: DownloadItem[]): QueueSummary {
  let downloading = 0
  let queued = 0
  let failed = 0
  let progressSum = 0
  let progressCount = 0
  let speed = 0
  let maxEta = 0
  for (const i of items) {
    if (i.status === 'downloading') {
      downloading++
      progressSum += i.progress
      progressCount++
      // Raw numbers carried straight from main (H4) — sum the rates, take the
      // longest ETA; no string round-trip.
      speed += i.speedBytesPerSec ?? 0
      maxEta = Math.max(maxEta, i.etaSeconds ?? 0)
    } else if (i.status === 'queued') {
      queued++
      progressCount++
    } else if (i.status === 'error') {
      failed++
    }
  }
  return {
    downloading,
    queued,
    failed,
    active: downloading > 0 || queued > 0,
    progress: progressCount > 0 ? progressSum / progressCount : 0,
    // Empty label (not '0 B/s' / '0:00') when nothing is actively transferring.
    speedLabel: speed > 0 ? (fmtSpeed(speed) ?? '') : '',
    etaLabel: maxEta > 0 ? (fmtEta(maxEta) ?? '') : ''
  }
}

// A 1-entry memo of the last summary, keyed on the `items` array reference. The
// store hands out a new `items` array on every change, and both App's taskbar
// subscription and DownloadsView's render summarize that same array per change — so
// caching by reference computes the O(n) aggregate once per change instead of twice
// (PERF4 / L94). `summarizeQueue` stays pure for the unit tests.
let cachedItems: DownloadItem[] | null = null
let cachedSummary: QueueSummary | null = null

/** Memoized `summarizeQueue`: recomputes only when the `items` reference changes. */
export function queueSummaryOf(items: DownloadItem[]): QueueSummary {
  if (items === cachedItems && cachedSummary) return cachedSummary
  cachedItems = items
  cachedSummary = summarizeQueue(items)
  return cachedSummary
}

// --- Duplicate detection ----------------------------------------------------

function normalizeUrl(url: string): string {
  try {
    const u = new URL(url)
    const host = u.hostname.replace(/^www\./, '').toLowerCase()
    return `${u.protocol}//${host}${u.pathname.replace(/\/$/, '')}${u.search}`
  } catch {
    return url.trim()
  }
}

/**
 * True when two URLs point at the same video: a matching YouTube id when both
 * are YouTube links, otherwise normalised-URL equality (host lower-cased, `www.`
 * and a trailing slash stripped). Powers the "already in your queue" guard.
 */
export function sameVideo(a: string, b: string): boolean {
  if (a === b) return true
  const ya = youtubeId(a)
  const yb = youtubeId(b)
  if (ya && yb) return ya === yb
  return normalizeUrl(a) === normalizeUrl(b)
}
