import { useEffect, useRef, useState } from 'react'
import { newId } from '../lib/id'

export type LineKind = 'stdout' | 'stderr' | 'cmd' | 'sys'
export interface Line {
  id: number
  text: string
  kind: LineKind
}
let lineSeq = 0
const mkLine = (text: string, kind: LineKind): Line => ({ id: ++lineSeq, text, kind })

// Verbose yt-dlp runs (--verbose, -F on a large channel) can emit thousands of
// lines in seconds. Cap the visible log to prevent unbounded React state growth
// and keep the <pre> scrollable (H6); older lines are dropped from the front.
const MAX_LOG_LINES = 2000

export interface TerminalRunController {
  args: string
  setArgs: (args: string) => void
  lines: Line[]
  running: boolean
  run: () => void
  stop: () => void
  clearLines: () => void
}

/**
 * View-model for the built-in yt-dlp terminal (L93/CC13): owns the run/stream/
 * stop orchestration and the capped output log so TerminalView stays
 * presentational — no `window.api` in the component.
 */
export function useTerminalRun(): TerminalRunController {
  const [args, setArgs] = useState('')
  const [lines, setLines] = useState<Line[]>([])
  const [running, setRunning] = useState(false)
  const runId = useRef<string | null>(null)

  // Stream terminal output for the current run into the log.
  useEffect(
    () =>
      window.api.onTerminalOutput((ev) => {
        if (ev.id !== runId.current) return
        if (ev.type === 'output') {
          setLines((ls) => [...ls, mkLine(ev.line, ev.stream)].slice(-MAX_LOG_LINES))
        } else if (ev.type === 'error') {
          setLines((ls) => [...ls, mkLine(ev.error, 'stderr')].slice(-MAX_LOG_LINES))
          setRunning(false)
          runId.current = null
        } else {
          setLines((ls) =>
            [...ls, mkLine(`-- exited (code ${ev.code ?? '?'})`, 'sys')].slice(-MAX_LOG_LINES)
          )
          setRunning(false)
          runId.current = null
        }
      }),
    []
  )

  function run(): void {
    const a = args.trim()
    if (!a || running) return
    const id = newId('t')
    runId.current = id
    setLines((ls) => [...ls, mkLine(`> yt-dlp ${a}`, 'cmd')])
    setRunning(true)
    window.api
      .runTerminal(id, a)
      .then((res) => {
        if (!res.ok) {
          setLines((ls) => [...ls, mkLine(res.error ?? 'Failed to start.', 'stderr')])
          setRunning(false)
          runId.current = null
        }
      })
      .catch((e: unknown) => {
        setLines((ls) => [...ls, mkLine(String(e), 'stderr')])
        setRunning(false)
        runId.current = null
      })
  }

  function stop(): void {
    if (runId.current) window.api.cancelTerminal(runId.current)
  }

  return { args, setArgs, lines, running, run, stop, clearLines: () => setLines([]) }
}
