/**
 * useTerminalRenderer
 *
 * Composable dedicado al renderizado de xterm.js para Claude Code.
 * TODA la lógica de renderizado del terminal debe estar aquí.
 *
 * Problemas conocidos de xterm.js que este composable resuelve:
 * - xterm.js no puede calcular dimensiones cuando el contenedor está oculto
 * - fit() no funciona correctamente con display:none
 * - El contenido se corrompe al cambiar visibilidad
 *
 * Referencias:
 * - https://github.com/xtermjs/xterm.js/issues/3029
 * - https://github.com/xtermjs/xterm.js/issues/664
 * - https://github.com/xtermjs/xterm.js/issues/3653
 */

import { ref, type Ref, nextTick } from 'vue'
import { Terminal } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
import { WebLinksAddon } from '@xterm/addon-web-links'
import '@xterm/xterm/css/xterm.css'

// ============================================================================
// TYPES
// ============================================================================

export interface TerminalTheme {
  background?: string
  foreground?: string
  cursor?: string
  cursorAccent?: string
  selectionBackground?: string
  black?: string
  red?: string
  green?: string
  yellow?: string
  blue?: string
  magenta?: string
  cyan?: string
  white?: string
  brightBlack?: string
  brightRed?: string
  brightGreen?: string
  brightYellow?: string
  brightBlue?: string
  brightMagenta?: string
  brightCyan?: string
  brightWhite?: string
}

export interface TerminalRendererOptions {
  container: Ref<HTMLElement | null>
  onData?: (data: string) => void
  onResize?: (cols: number, rows: number) => void
  onKeyEvent?: (e: KeyboardEvent) => boolean | void
  theme?: TerminalTheme
  fontSize?: number
  fontFamily?: string
}

export interface TerminalRenderer {
  // State
  terminal: Ref<Terminal | null>
  fitAddon: Ref<FitAddon | null>
  isReady: Ref<boolean>

  // Lifecycle
  init: () => boolean
  dispose: () => void

  // Writing
  write: (data: string) => Promise<void>
  writeln: (data: string) => void

  // Buffer management
  clear: () => void
  reset: () => void

  // Display
  fit: () => void
  refresh: () => void
  scrollToBottom: () => void
  scrollLines: (lines: number) => void

  // Focus
  focus: () => void
  blur: () => void

  // Selection
  getSelection: () => string

  // Buffer content
  getBufferContent: () => string

  // Visibility handling
  onBecameVisible: () => void

  // Replay handling
  handleReplay: (data: string) => void
}

// ============================================================================
// TERMINAL THEME
// ============================================================================

const TERMINAL_THEME = {
  background: 'rgba(12, 12, 12, 0.95)',
  foreground: '#ffffff',
  cursor: '#ffffff',
  cursorAccent: '#000000',
  selectionBackground: 'rgba(100, 150, 255, 0.4)',
  black: '#0c0c0c',
  red: '#c50f1f',
  green: '#13a10e',
  yellow: '#c19c00',
  blue: '#0037da',
  magenta: '#881798',
  cyan: '#3a96dd',
  white: '#cccccc',
  brightBlack: '#767676',
  brightRed: '#e74856',
  brightGreen: '#16c60c',
  brightYellow: '#f9f1a5',
  brightBlue: '#3b78ff',
  brightMagenta: '#b4009e',
  brightCyan: '#61d6d6',
  brightWhite: '#f2f2f2'
}

function getTerminalConfig(options: TerminalRendererOptions) {
  return {
    cursorBlink: true,
    cursorStyle: 'block' as const,
    fontSize: options.fontSize || 12,
    fontFamily: options.fontFamily || "'Consolas', 'Lucida Console', monospace",
    scrollback: 10000,
    theme: options.theme || TERMINAL_THEME,
    allowProposedApi: true
  }
}

// ============================================================================
// COMPOSABLE
// ============================================================================

export function useTerminalRenderer(options: TerminalRendererOptions): TerminalRenderer {
  const terminal = ref<Terminal | null>(null)
  const fitAddon = ref<FitAddon | null>(null)
  const isReady = ref(false)

  let resizeObserver: ResizeObserver | null = null

  // ==========================================================================
  // LIFECYCLE
  // ==========================================================================

  /**
   * Initialize the terminal.
   * IMPORTANT: Only call this when the container is VISIBLE!
   */
  function init(): boolean {
    if (!options.container.value) return false
    if (terminal.value) return true

    terminal.value = new Terminal(getTerminalConfig(options))

    fitAddon.value = new FitAddon()
    terminal.value.loadAddon(fitAddon.value)
    terminal.value.loadAddon(new WebLinksAddon())

    terminal.value.open(options.container.value)

    // Don't fit here - wait for onBecameVisible() when container is fully sized
    nextTick(() => {
      isReady.value = true
    })

    // Setup resize observer (ignore tiny sizes during transitions)
    resizeObserver = new ResizeObserver(() => {
      if (fitAddon.value && terminal.value) {
        const prevCols = terminal.value.cols
        const prevRows = terminal.value.rows
        fitAddon.value.fit()
        // Only notify if size is reasonable (not during CSS transitions)
        if (terminal.value.cols >= 20 && terminal.value.rows >= 5) {
          if (terminal.value.cols !== prevCols || terminal.value.rows !== prevRows) {
            options.onResize?.(terminal.value.cols, terminal.value.rows)
          }
        }
      }
    })
    resizeObserver.observe(options.container.value)

    // Handle user input
    if (options.onData) {
      terminal.value.onData(options.onData)
    }

    // Handle custom key events
    if (options.onKeyEvent) {
      terminal.value.attachCustomKeyEventHandler((e) => {
        const result = options.onKeyEvent!(e)
        return result === undefined ? true : result
      })
    }

    return true
  }

  /**
   * Dispose the terminal and cleanup resources.
   */
  function dispose(): void {
    resizeObserver?.disconnect()
    resizeObserver = null
    if (terminal.value) {
      try { terminal.value.dispose() } catch { /* addon not loaded */ }
      terminal.value = null
    }
    fitAddon.value = null
    isReady.value = false
  }

  // ==========================================================================
  // WRITING
  // ==========================================================================

  /**
   * Write data to terminal with Promise-based completion.
   */
  function write(data: string): Promise<void> {
    return new Promise((resolve) => {
      if (!terminal.value) {
        resolve()
        return
      }
      terminal.value.write(data, resolve)
    })
  }

  /**
   * Write a line to the terminal (adds \r\n).
   */
  function writeln(data: string): void {
    terminal.value?.writeln(data)
  }

  // ==========================================================================
  // BUFFER MANAGEMENT
  // ==========================================================================

  /**
   * Clear the terminal scrollback buffer.
   */
  function clear(): void {
    terminal.value?.clear()
  }

  /**
   * Reset the terminal completely.
   */
  function reset(): void {
    terminal.value?.reset()
  }

  // ==========================================================================
  // DISPLAY
  // ==========================================================================

  /**
   * Fit terminal dimensions to container size.
   */
  function fit(): void {
    if (!fitAddon.value) return
    fitAddon.value.fit()
  }

  /**
   * Refresh all visible rows.
   */
  function refresh(): void {
    if (!terminal.value) return
    terminal.value.refresh(0, terminal.value.rows - 1)
  }

  /**
   * Scroll to the bottom of the terminal.
   */
  function scrollToBottom(): void {
    terminal.value?.scrollToBottom()
  }

  /**
   * Scroll by a number of lines (positive = down, negative = up).
   */
  function scrollLines(lines: number): void {
    terminal.value?.scrollLines(lines)
  }

  // ==========================================================================
  // FOCUS
  // ==========================================================================

  function focus(): void {
    terminal.value?.focus()
  }

  function blur(): void {
    terminal.value?.blur()
  }

  // ==========================================================================
  // SELECTION
  // ==========================================================================

  function getSelection(): string {
    return terminal.value?.getSelection() || ''
  }

  /**
   * Get all content from the terminal buffer.
   * Useful for copying terminal content.
   */
  function getBufferContent(): string {
    if (!terminal.value) return ''

    const buffer = terminal.value.buffer.active
    let content = ''

    for (let i = 0; i < buffer.length; i++) {
      const line = buffer.getLine(i)
      if (line) {
        content += line.translateToString(true) + '\n'
      }
    }

    return content.trim()
  }

  // ==========================================================================
  // VISIBILITY HANDLING - THE MAIN PROBLEM WE'RE SOLVING
  // ==========================================================================

  /**
   * Handle terminal becoming visible after being hidden.
   * Simple: fit + refresh + focus
   */
  function onBecameVisible(): void {
    if (!terminal.value || !fitAddon.value) return

    fit()
    terminal.value.refresh(0, terminal.value.rows - 1)
    focus()
  }

  // ==========================================================================
  // REPLAY HANDLING
  // ==========================================================================

  /**
   * Handle replay data from server.
   * Writes in chunks to avoid overwhelming xterm.js with large buffers.
   */
  function handleReplay(data: string): void {
    if (!terminal.value) return

    const CHUNK_SIZE = 8192 // 8KB chunks
    const CHUNK_DELAY = 10  // 10ms between chunks

    // Small data: write directly
    if (data.length <= CHUNK_SIZE) {
      terminal.value.write(data, () => {
        terminal.value?.refresh(0, terminal.value.rows - 1)
        terminal.value?.scrollToBottom()
      })
      return
    }

    // Large data: write in chunks
    let offset = 0
    const writeNextChunk = () => {
      if (!terminal.value || offset >= data.length) {
        // Done - final refresh and scroll
        terminal.value?.refresh(0, terminal.value.rows - 1)
        terminal.value?.scrollToBottom()
        return
      }

      const chunk = data.slice(offset, offset + CHUNK_SIZE)
      offset += CHUNK_SIZE

      terminal.value.write(chunk, () => {
        setTimeout(writeNextChunk, CHUNK_DELAY)
      })
    }

    writeNextChunk()
  }

  // ==========================================================================
  // RETURN
  // ==========================================================================

  return {
    // State
    terminal,
    fitAddon,
    isReady,

    // Lifecycle
    init,
    dispose,

    // Writing
    write,
    writeln,

    // Buffer management
    clear,
    reset,

    // Display
    fit,
    refresh,
    scrollToBottom,
    scrollLines,

    // Focus
    focus,
    blur,

    // Selection
    getSelection,

    // Buffer content
    getBufferContent,

    // Visibility handling
    onBecameVisible,

    // Replay handling
    handleReplay
  }
}
