/**
 * useEphemeralTerminal
 *
 * Composable for terminal sessions that can be parked (WS closed,
 * renderer disposed) and later reconnected to the same server PTY
 * via buffer replay.
 *
 * Used by ResumeTerminalButton to open `<agent> --resume <sessionId>`.
 */

import { ref, computed, type Ref } from 'vue'
import { useTerminalRenderer, type TerminalRenderer } from './useTerminalRenderer'
import { resolveEndpoints, terminalApiUrl } from '../config/endpoints'
import { apiFetch } from '@/lib/tauri'

export type EphemeralState = 'off' | 'connecting' | 'shell-ready' | 'running' | 'exited'

export interface EphemeralTerminal {
  state: Ref<EphemeralState>
  connected: Ref<boolean>
  containerRef: Ref<HTMLElement | null>
  renderer: TerminalRenderer
  ephemeralSessionId: string

  /** Connect WS, wait for shell, then auto-run the resume command */
  start: () => void

  /** Send text + \r directly to the terminal's WebSocket */
  sendInput: (text: string) => void

  /** Send raw bytes to the terminal without appending \r */
  sendRaw: (data: string) => void

  /** Ctrl+C, exit, close WS, kill session on server */
  stop: () => Promise<void>

  /** Full cleanup (stop + dispose renderer) */
  dispose: () => Promise<void>

  /** Park: close WS + dispose renderer, but keep server PTY alive for reconnection */
  park: () => void
}

export function useEphemeralTerminal(
  command: string,
  existingSessionId?: string,
  options?: {
    onExtraMessage?: (msg: Record<string, any>) => void
  }
): EphemeralTerminal {
  const containerRef = ref<HTMLElement | null>(null)
  const connected = ref(false)
  const state = ref<EphemeralState>('off')

  const ephemeralSessionId = existingSessionId || `resume-${Date.now()}`
  const isReconnect = !!existingSessionId

  let socket: WebSocket | null = null

  const renderer = useTerminalRenderer({
    container: containerRef,
    onData: (data) => {
      if (socket?.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify({ type: 'input', data }))
      }
    },
    onResize: (cols, rows) => {
      if (socket?.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify({ type: 'resize', cols, rows }))
      }
    },
    onKeyEvent: (e) => {
      // Ctrl+V: Paste
      if (e.ctrlKey && e.key === 'v' && e.type === 'keydown') {
        e.preventDefault()
        navigator.clipboard.readText().then((text) => {
          if (text && socket?.readyState === WebSocket.OPEN) {
            socket.send(JSON.stringify({ type: 'input', data: text }))
          }
        }).catch(console.error)
        return false
      }
      // Ctrl+C: Copy selection
      if (e.ctrlKey && e.key === 'c' && e.type === 'keydown') {
        const sel = renderer.getSelection()
        if (sel) {
          navigator.clipboard.writeText(sel).catch(console.error)
          return false
        }
      }
      return true
    }
  })

  function start() {
    if (state.value !== 'off') return
    state.value = 'connecting'

    const wsBase = resolveEndpoints().terminal
    const sep = wsBase.includes('?') ? '&' : '?'
    const wsUrl = `${wsBase}${sep}session=${ephemeralSessionId}`

    socket = new WebSocket(wsUrl)

    socket.onopen = () => {
      connected.value = true
      const term = renderer.terminal.value
      if (term) {
        socket?.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows }))
      }
    }

    socket.onmessage = (event) => {
      try {
        const msg = JSON.parse(event.data)
        switch (msg.type) {
          case 'connected':
            state.value = 'shell-ready'
            if (isReconnect) {
              // Reconnecting to existing PTY — request buffer replay
              if (socket?.readyState === WebSocket.OPEN) {
                socket.send(JSON.stringify({ type: 'request-replay', tailOnly: false }))
              }
              state.value = 'running'
            } else {
              // First time — wait for shell prompt, then send command
              setTimeout(() => {
                if (socket?.readyState === WebSocket.OPEN) {
                  socket.send(JSON.stringify({ type: 'input', data: command + '\r' }))
                }
                state.value = 'running'
              }, 500)
            }
            break
          case 'replay':
            renderer.handleReplay(msg.data || '')
            break
          case 'output':
            renderer.write(msg.data)
            break
          case 'exit':
            renderer.write(msg.data)
            state.value = 'exited'
            break
          case 'error':
            renderer.writeln(`\x1b[31mError: ${msg.message}\x1b[0m`)
            break
          default:
            options?.onExtraMessage?.(msg)
            break
        }
      } catch { /* ignore parse errors */ }
    }

    socket.onclose = () => {
      connected.value = false
      if (state.value !== 'off') state.value = 'exited'
    }

    socket.onerror = () => {
      state.value = 'off'
    }
  }

  function sendInput(text: string) {
    if (!socket || socket.readyState !== WebSocket.OPEN) return
    // Send text first, then Enter as a separate message —
    // sending them together makes Claude Code treat \r as a
    // literal line-break inside the prompt text.
    socket.send(JSON.stringify({ type: 'input', data: text }))
    setTimeout(() => {
      if (socket?.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify({ type: 'input', data: '\r' }))
      }
    }, 80)
  }

  function sendRaw(data: string) {
    if (!socket || socket.readyState !== WebSocket.OPEN) return
    socket.send(JSON.stringify({ type: 'input', data }))
  }

  async function stop() {
    if (!socket || socket.readyState !== WebSocket.OPEN) {
      state.value = 'off'
      return
    }

    // Send Ctrl+C to interrupt running process
    socket.send(JSON.stringify({ type: 'input', data: '\x03' }))
    await new Promise(r => setTimeout(r, 300))

    // Send exit to close the shell
    socket.send(JSON.stringify({ type: 'input', data: 'exit\r' }))
    await new Promise(r => setTimeout(r, 300))

    // Close WebSocket
    socket.onclose = null
    socket.close()
    socket = null
    connected.value = false
    state.value = 'off'

    // Force-kill via HTTP as safety net
    try {
      await apiFetch(terminalApiUrl('/kill-session'), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ sessionId: ephemeralSessionId })
      })
    } catch { /* best effort */ }
  }

  async function dispose() {
    await stop()
    renderer.dispose()
  }

  function park() {
    // Close WS without killing server PTY — it stays alive for reconnection
    if (socket) {
      socket.onclose = null
      socket.close()
      socket = null
    }
    connected.value = false
    renderer.dispose()
    state.value = 'off'
  }

  return {
    state,
    connected,
    containerRef,
    renderer,
    ephemeralSessionId,
    start,
    sendInput,
    sendRaw,
    stop,
    dispose,
    park
  }
}
