/**
 * React hook for WebSocket communication with agent
 */

import { useState, useEffect, useCallback, useRef } from 'react'
import type { AgentEvent } from '@/lib/agents/bandit-state'
import type { TerminalLine, ChatMessage } from '@/lib/websocket/agent-events'
import { handleAgentEvent } from '@/lib/websocket/agent-events'

export type ConnectionState = 'connecting' | 'connected' | 'disconnected' | 'error'

export interface UseAgentWebSocketReturn {
  connectionState: ConnectionState
  sendCommand: (command: string) => void
  sendMessage: (message: string) => void
  terminalLines: TerminalLine[]
  chatMessages: ChatMessage[]
  setTerminalLines: React.Dispatch<React.SetStateAction<TerminalLine[]>>
  setChatMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>
}

export function useAgentWebSocket(runId: string | null): UseAgentWebSocketReturn {
  const [socket, setSocket] = useState<WebSocket | null>(null)
  const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected')
  const [terminalLines, setTerminalLines] = useState<TerminalLine[]>([])
  const [chatMessages, setChatMessages] = useState<ChatMessage[]>([])
  const reconnectTimeoutRef = useRef<NodeJS.Timeout>()
  const reconnectAttemptsRef = useRef(0)

  // Send command to terminal
  const sendCommand = useCallback((command: string) => {
    if (socket && socket.readyState === WebSocket.OPEN) {
      socket.send(JSON.stringify({ 
        type: 'manual_command', 
        command,
        timestamp: new Date().toISOString(),
      }))
    }
  }, [socket])

  // Send message to agent chat
  const sendMessage = useCallback((message: string) => {
    if (socket && socket.readyState === WebSocket.OPEN) {
      socket.send(JSON.stringify({ 
        type: 'user_message', 
        message,
        timestamp: new Date().toISOString(),
      }))
    }
  }, [socket])

  // Connect to WebSocket
  const connect = useCallback(() => {
    if (!runId) return

    try {
      setConnectionState('connecting')
      
      // Determine WebSocket URL (ws:// for http://, wss:// for https://)
      const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
      const wsUrl = `${protocol}//${window.location.host}/api/agent/${runId}/ws`
      
      const ws = new WebSocket(wsUrl)

      ws.onopen = () => {
        console.log('✅ WebSocket connected to:', wsUrl)
        setConnectionState('connected')
        reconnectAttemptsRef.current = 0

        // Send ping every 30 seconds to keep connection alive
        const pingInterval = setInterval(() => {
          if (ws.readyState === WebSocket.OPEN) {
            ws.send(JSON.stringify({ type: 'ping' }))
          } else {
            clearInterval(pingInterval)
          }
        }, 30000)
      }

      ws.onmessage = (event) => {
        console.log('📨 WebSocket message received:', event.data)
        try {
          const agentEvent: AgentEvent = JSON.parse(event.data)
          console.log('📦 Parsed event:', agentEvent.type, agentEvent.data)
          
          // Handle different event types
          handleAgentEvent(
            agentEvent,
            setTerminalLines,
            setChatMessages
          )
        } catch (error) {
          console.error('❌ Error parsing WebSocket message:', error)
        }
      }

      ws.onerror = (error) => {
        console.error('WebSocket error:', error)
        setConnectionState('error')
      }

      ws.onclose = () => {
        console.log('WebSocket disconnected')
        setConnectionState('disconnected')
        setSocket(null)

        // Auto-reconnect with exponential backoff
        if (reconnectAttemptsRef.current < 5) {
          const delay = Math.min(1000 * Math.pow(2, reconnectAttemptsRef.current), 10000)
          console.log(`Reconnecting in ${delay}ms...`)
          
          reconnectTimeoutRef.current = setTimeout(() => {
            reconnectAttemptsRef.current++
            connect()
          }, delay)
        }
      }

      setSocket(ws)
    } catch (error) {
      console.error('Error connecting to WebSocket:', error)
      setConnectionState('error')
    }
  }, [runId])

  // Connect when runId changes
  useEffect(() => {
    if (runId) {
      connect()
    }

    // Cleanup on unmount or runId change
    return () => {
      if (reconnectTimeoutRef.current) {
        clearTimeout(reconnectTimeoutRef.current)
      }
      if (socket) {
        socket.close()
      }
    }
  }, [runId, connect])

  return {
    connectionState,
    sendCommand,
    sendMessage,
    terminalLines,
    chatMessages,
    setTerminalLines,
    setChatMessages,
  }
}

