import { useEffect, useRef, type RefObject } from 'react'
import type { MediaPlayerInstance } from '@vidstack/react'

/**
 * Renders ASS / SSA subtitles via libass-wasm (SubtitlesOctopus). The
 * library spawns a Web Worker that compiles libass to WASM and renders
 * each frame to an offscreen canvas, which it then transfers to the
 * onscreen canvas we mount over the video.
 *
 * Why this exists: Jellyfin can transcode ASS to VTT for browsers that
 * don't render ASS natively, but VTT loses the things that matter most
 * about ASS - positioning, fonts, karaoke, overlapping cues, styled
 * effects. For anime / fansub libraries this is the difference between
 * "subtitles work" and "subtitles look right".
 *
 * The component takes a direct ASS URL (we construct it via
 * getSubtitleUrl(..., 'ass') so Jellyfin returns the original) and the
 * underlying <video> element. Mounts a fixed canvas overlay sized to
 * match the player.
 */

interface Props {
  playerRef: RefObject<MediaPlayerInstance | null>
  /** URL pointing to a Jellyfin ASS/SSA stream. Pass null to disable. */
  subtitleUrl: string | null
}

const WORKER_URL = '/libass/subtitles-octopus-worker.js'
const LEGACY_WORKER_URL = '/libass/subtitles-octopus-worker-legacy.js'

export default function LibAssRenderer({ playerRef, subtitleUrl }: Props) {
  const canvasRef = useRef<HTMLCanvasElement | null>(null)
  const octopusRef = useRef<any>(null)

  useEffect(() => {
    let cancelled = false
    if (!subtitleUrl) return

    function getVideo(): HTMLVideoElement | null {
      const player = playerRef.current
      const el = (player as { el?: HTMLElement } | null)?.el
      return (el?.querySelector('video') as HTMLVideoElement | null) || null
    }

    async function init() {
      const video = getVideo()
      const canvas = canvasRef.current
      if (!video || !canvas) return
      try {
        // SubtitlesOctopus is exposed as a global by the dist bundle. We
        // dynamic-import the npm package so the wasm worker isn't pulled
        // into the main bundle for users who never see ASS subs.
        const mod = await import('libass-wasm') as { default?: unknown; SubtitlesOctopus?: unknown }
        const SubtitlesOctopus = (mod.default || mod.SubtitlesOctopus || window.SubtitlesOctopus) as new (opts: Record<string, unknown>) => { dispose(): void }
        if (!SubtitlesOctopus || cancelled) return
        if (octopusRef.current) {
          try { octopusRef.current.dispose() } catch { /* noop */ }
          octopusRef.current = null
        }
        octopusRef.current = new SubtitlesOctopus({
          video,
          canvas,
          subUrl: subtitleUrl,
          workerUrl: WORKER_URL,
          legacyWorkerUrl: LEGACY_WORKER_URL,
          // Render at 1.5x for sharper text on high-density displays
          targetFps: 24,
        })
      } catch (err) {
        console.warn('[libass] failed to init', err)
      }
    }
    init()

    return () => {
      cancelled = true
      if (octopusRef.current) {
        try { octopusRef.current.dispose() } catch { /* noop */ }
        octopusRef.current = null
      }
    }
  }, [playerRef, subtitleUrl])

  if (!subtitleUrl) return null
  return (
    <canvas
      ref={canvasRef}
      className="absolute inset-0 w-full h-full pointer-events-none z-[100]"
      style={{ transform: 'translateZ(0)', willChange: 'transform', backfaceVisibility: 'hidden' }}
      aria-hidden
    />
  )
}
