/**
 * LiveLogTail — React wrapper around the vanilla `LogViewer`.
 * @countermeasure-platform/web-components/primitives/live-log-tail
 *
 * Mounts the CodeMirror-backed `LogViewer` inside a container `<div>` and
 * forwards the lifecycle. Imperative `append(line)` and `clear()` are
 * exposed on the ref so SSE consumers can stream lines without rerendering.
 *
 * The `lineParser` prop lets a host map a raw line to
 * `{ className?, text }` for severity coloring; the underlying viewer's
 * built-in severity tokenizer is bypassed when a parser is supplied —
 * the mapped class is set as a `data-cm-line-class` on a wrapper span.
 *
 * `onScrollAway` fires when the user scrolls the viewer away from the
 * bottom (i.e. follow-mode broken), so the host can offer a "jump to
 * latest" affordance.
 */

import * as React from 'react'
import { LogViewer } from '../editors/log-viewer'
import type { LogSeverity } from '../editors/types'
import { cn } from '../react/primitives/utils'

export interface LiveLogTailParseResult {
  className?: string
  text: string
  severity?: LogSeverity
}

export interface LiveLogTailProps extends React.HTMLAttributes<HTMLDivElement> {
  /** Initial lines to seed into the buffer. */
  lines?: string[]
  /** Maximum lines retained in the ring buffer. Defaults to 10000. */
  maxLines?: number
  /** Auto-scroll to the new line on append. Defaults to true. */
  follow?: boolean
  /** Optional custom parser per line. */
  lineParser?: (line: string) => LiveLogTailParseResult
  /** Fires when the user scrolls away from the bottom (follow-mode breaks). */
  onScrollAway?: () => void
}

export interface LiveLogTailHandle {
  append: (line: string) => void
  clear: () => void
}

export const LiveLogTail = React.forwardRef<LiveLogTailHandle, LiveLogTailProps>(
  function LiveLogTail(
    { lines, maxLines, follow = true, lineParser, onScrollAway, className, ...rest },
    ref
  ) {
    const containerRef = React.useRef<HTMLDivElement | null>(null)
    const viewerRef = React.useRef<LogViewer | null>(null)
    const parserRef = React.useRef<typeof lineParser>(lineParser)
    const onScrollAwayRef = React.useRef<typeof onScrollAway>(onScrollAway)

    React.useEffect(() => {
      parserRef.current = lineParser
    }, [lineParser])

    React.useEffect(() => {
      onScrollAwayRef.current = onScrollAway
    }, [onScrollAway])

    // Mount once.
    React.useEffect(() => {
      const container = containerRef.current
      if (!container) return

      const viewer = new LogViewer({
        container,
        maxLines: maxLines ?? 10000,
        followMode: follow,
        severityColors: true,
        value: lines && lines.length > 0 ? lines.join('\n') : '',
      })
      viewerRef.current = viewer

      // Detect scroll-away — when the user scrolls up, fire onScrollAway.
      const view = viewer.getView()
      const scrollEl = view.scrollDOM
      let lastFiredAway = false
      const onScroll = () => {
        const distanceFromBottom =
          scrollEl.scrollHeight - (scrollEl.scrollTop + scrollEl.clientHeight)
        const isAway = distanceFromBottom > 16
        if (isAway && !lastFiredAway) {
          lastFiredAway = true
          onScrollAwayRef.current?.()
        } else if (!isAway && lastFiredAway) {
          lastFiredAway = false
        }
      }
      scrollEl.addEventListener('scroll', onScroll, { passive: true })

      return () => {
        scrollEl.removeEventListener('scroll', onScroll)
        viewer.destroy()
        viewerRef.current = null
      }
      // Mount-only — `lines` / `maxLines` / `follow` updates handled by the
      // separate effects below; rebuilding the editor on every change would
      // throw away buffered output.
    }, [])

    // Push follow-mode changes to the viewer.
    React.useEffect(() => {
      viewerRef.current?.setFollowMode(follow)
    }, [follow])

    // Push maxLines changes to the viewer.
    React.useEffect(() => {
      if (typeof maxLines === 'number') {
        viewerRef.current?.setMaxLines(maxLines)
      }
    }, [maxLines])

    React.useImperativeHandle(
      ref,
      () => ({
        append(line: string) {
          const viewer = viewerRef.current
          if (!viewer) return
          const parser = parserRef.current
          if (parser) {
            const parsed = parser(line)
            viewer.appendLine(parsed.text, parsed.severity)
          } else {
            viewer.appendLine(line)
          }
        },
        clear() {
          viewerRef.current?.clear()
        },
      }),
      []
    )

    return (
      <div
        ref={containerRef}
        data-slot="live-log-tail"
        className={cn(
          'cmm-live-log-tail relative h-full min-h-[200px] w-full overflow-hidden rounded-md border border-border bg-muted/40 font-mono text-xs',
          className
        )}
        {...rest}
      />
    )
  }
)
