/**
 * WebSocket Composable
 *
 * Manages Socket.io connection with type safety, authentication, and auto-reconnection.
 * This composable is league-agnostic and will be shared between SBA and PD frontends.
 *
 * Features:
 * - Type-safe Socket.io client (TypedSocket)
 * - JWT authentication integration
 * - Auto-reconnection with exponential backoff
 * - Connection status tracking
 * - Event listener lifecycle management
 * - Integration with game store
 */

import { ref, computed, watch, onUnmounted } from 'vue'
import { io, Socket } from 'socket.io-client'
import type {
  TypedSocket,
  ClientToServerEvents,
  ServerToClientEvents,
} from '~/types'
import { useAuthStore } from '~/store/auth'
import { useGameStore } from '~/store/game'
import { useUiStore } from '~/store/ui'

// Reconnection configuration
const RECONNECTION_DELAY_BASE = 1000 // Start with 1 second
const RECONNECTION_DELAY_MAX = 30000 // Max 30 seconds
const MAX_RECONNECTION_ATTEMPTS = 10

// Singleton socket instance
let socketInstance: Socket<ServerToClientEvents, ClientToServerEvents> | null = null
let reconnectionAttempts = 0
let reconnectionTimeout: NodeJS.Timeout | null = null

export function useWebSocket() {
  // ============================================================================
  // State
  // ============================================================================

  const isConnected = ref(false)
  const isConnecting = ref(false)
  const connectionError = ref<string | null>(null)
  const lastConnectionAttempt = ref<number | null>(null)

  // ============================================================================
  // Stores
  // ============================================================================

  const authStore = useAuthStore()
  const gameStore = useGameStore()
  const uiStore = useUiStore()

  // ============================================================================
  // Computed
  // ============================================================================

  const socket = computed((): TypedSocket | null => {
    return socketInstance as TypedSocket | null
  })

  const canConnect = computed(() => {
    return authStore.isAuthenticated && authStore.isTokenValid
  })

  const shouldReconnect = computed(() => {
    return (
      !isConnected.value &&
      canConnect.value &&
      reconnectionAttempts < MAX_RECONNECTION_ATTEMPTS
    )
  })

  // ============================================================================
  // Connection Management
  // ============================================================================

  /**
   * Connect to WebSocket server with JWT authentication
   */
  function connect() {
    if (!canConnect.value) {
      console.warn('[WebSocket] Cannot connect: not authenticated or token invalid')
      return
    }

    if (isConnected.value || isConnecting.value) {
      console.warn('[WebSocket] Already connected or connecting')
      return
    }

    isConnecting.value = true
    connectionError.value = null
    lastConnectionAttempt.value = Date.now()

    const config = useRuntimeConfig()
    const wsUrl = config.public.wsUrl

    console.log('[WebSocket] Connecting to', wsUrl)

    // Create or reuse socket instance
    if (!socketInstance) {
      socketInstance = io(wsUrl, {
        auth: {
          token: authStore.token,
        },
        autoConnect: false,
        reconnection: false, // We handle reconnection manually for better control
        transports: ['websocket', 'polling'],
      })

      setupEventListeners()
    } else {
      // Update auth token if reconnecting
      socketInstance.auth = {
        token: authStore.token,
      }
    }

    // Connect
    socketInstance.connect()
  }

  /**
   * Disconnect from WebSocket server
   */
  function disconnect() {
    console.log('[WebSocket] Disconnecting')

    // Clear reconnection timer
    if (reconnectionTimeout) {
      clearTimeout(reconnectionTimeout)
      reconnectionTimeout = null
    }

    // Disconnect socket
    if (socketInstance) {
      socketInstance.disconnect()
    }

    isConnected.value = false
    isConnecting.value = false
    connectionError.value = null
    reconnectionAttempts = 0
  }

  /**
   * Reconnect with exponential backoff
   */
  function scheduleReconnection() {
    if (!shouldReconnect.value) {
      console.log('[WebSocket] Reconnection not needed or max attempts reached')
      return
    }

    // Calculate delay with exponential backoff
    const delay = Math.min(
      RECONNECTION_DELAY_BASE * Math.pow(2, reconnectionAttempts),
      RECONNECTION_DELAY_MAX
    )

    console.log(
      `[WebSocket] Scheduling reconnection attempt ${reconnectionAttempts + 1}/${MAX_RECONNECTION_ATTEMPTS} in ${delay}ms`
    )

    reconnectionTimeout = setTimeout(() => {
      reconnectionAttempts++
      connect()
    }, delay)
  }

  // ============================================================================
  // Event Listeners Setup
  // ============================================================================

  /**
   * Set up all WebSocket event listeners
   */
  function setupEventListeners() {
    if (!socketInstance) return

    // ========================================
    // Connection Events
    // ========================================

    socketInstance.on('connect', () => {
      console.log('[WebSocket] Connected successfully')
      isConnected.value = true
      isConnecting.value = false
      connectionError.value = null
      reconnectionAttempts = 0

      // Update game store
      gameStore.setConnected(true)

      // Show success toast
      uiStore.showSuccess('Connected to game server')
    })

    socketInstance.on('disconnect', (reason) => {
      console.log('[WebSocket] Disconnected:', reason)
      isConnected.value = false
      isConnecting.value = false

      // Update game store
      gameStore.setConnected(false)

      // Show warning toast
      uiStore.showWarning('Disconnected from game server')

      // Attempt reconnection if not intentional
      if (reason !== 'io client disconnect') {
        scheduleReconnection()
      }
    })

    socketInstance.on('connect_error', (error) => {
      console.error('[WebSocket] Connection error:', error)
      isConnected.value = false
      isConnecting.value = false
      connectionError.value = error.message

      // Update game store
      gameStore.setError(error.message)

      // Show error toast
      uiStore.showError(`Connection failed: ${error.message}`)

      // Attempt reconnection
      scheduleReconnection()
    })

    socketInstance.on('connected', (data) => {
      console.log('[WebSocket] Server confirmed connection for user:', data.user_id)
    })

    socketInstance.on('heartbeat_ack', () => {
      // Heartbeat acknowledged - connection is healthy
      // No action needed, just prevents timeout
    })

    // ========================================
    // Game State Events
    // ========================================

    socketInstance.on('game_state_update', (state) => {
      console.log('[WebSocket] Game state update received')
      gameStore.setGameState(state)
    })

    socketInstance.on('game_state_sync', (data) => {
      console.log('[WebSocket] Full game state sync received')
      gameStore.setGameState(data.state)
      // Add recent plays to history
      data.recent_plays.forEach((play) => {
        gameStore.addPlayToHistory(play)
      })
    })

    socketInstance.on('play_completed', (play) => {
      console.log('[WebSocket] Play completed:', play.description)
      gameStore.addPlayToHistory(play)
      uiStore.showInfo(play.description, 3000)
    })

    socketInstance.on('inning_change', (data) => {
      console.log(`[WebSocket] Inning change: ${data.half} ${data.inning}`)
      uiStore.showInfo(`${data.half === 'top' ? 'Top' : 'Bottom'} ${data.inning}`, 3000)
    })

    socketInstance.on('game_ended', (data) => {
      console.log('[WebSocket] Game ended:', data.winner_team_id)
      uiStore.showSuccess(
        `Game Over! Final: ${data.final_score.away} - ${data.final_score.home}`,
        10000
      )
    })

    // ========================================
    // Decision Events
    // ========================================

    socketInstance.on('decision_required', (prompt) => {
      console.log('[WebSocket] Decision required:', prompt.phase)
      gameStore.setDecisionPrompt(prompt)
    })

    socketInstance.on('defensive_decision_submitted', (data) => {
      console.log('[WebSocket] Defensive decision submitted')
      gameStore.clearDecisionPrompt()
      if (data.pending_decision) {
        uiStore.showInfo('Defense set. Waiting for offense...', 3000)
      } else {
        uiStore.showSuccess('Defense set. Ready to play!', 3000)
      }
    })

    socketInstance.on('offensive_decision_submitted', (data) => {
      console.log('[WebSocket] Offensive decision submitted')
      gameStore.clearDecisionPrompt()
      uiStore.showSuccess('Offense set. Ready to play!', 3000)
    })

    // ========================================
    // Manual Workflow Events
    // ========================================

    socketInstance.on('dice_rolled', (data) => {
      console.log('[WebSocket] Dice rolled:', data.roll_id)
      gameStore.setPendingRoll({
        roll_id: data.roll_id,
        d6_one: data.d6_one,
        d6_two_a: 0, // Not provided by server
        d6_two_b: 0, // Not provided by server
        d6_two_total: data.d6_two_total,
        chaos_d20: data.chaos_d20,
        resolution_d20: data.resolution_d20,
        check_wild_pitch: data.check_wild_pitch,
        check_passed_ball: data.check_passed_ball,
        timestamp: data.timestamp,
      })
      uiStore.showInfo(data.message, 5000)
    })

    socketInstance.on('outcome_accepted', (data) => {
      console.log('[WebSocket] Outcome accepted:', data.outcome)
      gameStore.clearPendingRoll()
      uiStore.showSuccess('Outcome submitted. Resolving play...', 3000)
    })

    // ========================================
    // Substitution Events
    // ========================================

    socketInstance.on('player_substituted', (data) => {
      console.log('[WebSocket] Player substituted:', data.type)
      uiStore.showInfo(data.message, 5000)
      // Request updated lineup
      if (socketInstance) {
        socketInstance.emit('get_lineup', {
          game_id: gameStore.gameId!,
          team_id: data.team_id,
        })
      }
    })

    socketInstance.on('substitution_confirmed', (data) => {
      console.log('[WebSocket] Substitution confirmed')
      uiStore.showSuccess(data.message, 5000)
    })

    // ========================================
    // Data Response Events
    // ========================================

    socketInstance.on('lineup_data', (data) => {
      console.log('[WebSocket] Lineup data received for team:', data.team_id)
      gameStore.updateLineup(data.team_id, data.players)
    })

    socketInstance.on('box_score_data', (data) => {
      console.log('[WebSocket] Box score data received')
      // Box score will be handled by dedicated component
      // Just log for now
    })

    // ========================================
    // Error Events
    // ========================================

    socketInstance.on('error', (data) => {
      console.error('[WebSocket] Server error:', data.message)
      gameStore.setError(data.message)
      uiStore.showError(data.message, 7000)
    })

    socketInstance.on('outcome_rejected', (data) => {
      console.error('[WebSocket] Outcome rejected:', data.message)
      uiStore.showError(`Invalid outcome: ${data.message}`, 7000)
    })

    socketInstance.on('substitution_error', (data) => {
      console.error('[WebSocket] Substitution error:', data.message)
      uiStore.showError(`Substitution failed: ${data.message}`, 7000)
    })

    socketInstance.on('invalid_action', (data) => {
      console.error('[WebSocket] Invalid action:', data.reason)
      uiStore.showError(`Invalid action: ${data.reason}`, 7000)
    })

    socketInstance.on('connection_error', (data) => {
      console.error('[WebSocket] Connection error:', data.error)
      connectionError.value = data.error
      uiStore.showError(`Connection error: ${data.error}`, 7000)
    })
  }

  // ============================================================================
  // Heartbeat
  // ============================================================================

  /**
   * Send periodic heartbeat to keep connection alive
   */
  let heartbeatInterval: NodeJS.Timeout | null = null

  function startHeartbeat() {
    if (heartbeatInterval) return

    heartbeatInterval = setInterval(() => {
      if (socketInstance?.connected) {
        socketInstance.emit('heartbeat')
      }
    }, 30000) // Every 30 seconds
  }

  function stopHeartbeat() {
    if (heartbeatInterval) {
      clearInterval(heartbeatInterval)
      heartbeatInterval = null
    }
  }

  // ============================================================================
  // Watchers
  // ============================================================================

  // Auto-connect when authenticated
  watch(
    () => authStore.isAuthenticated,
    (authenticated) => {
      if (authenticated && !isConnected.value) {
        connect()
        startHeartbeat()
      } else if (!authenticated && isConnected.value) {
        disconnect()
        stopHeartbeat()
      }
    },
    { immediate: false }
  )

  // ============================================================================
  // Lifecycle
  // ============================================================================

  onUnmounted(() => {
    console.log('[WebSocket] Component unmounted, cleaning up')
    stopHeartbeat()
    // Don't disconnect on unmount - keep connection alive for app
    // Only disconnect when explicitly requested or user logs out
  })

  // ============================================================================
  // Public API
  // ============================================================================

  return {
    // State
    socket,
    isConnected: readonly(isConnected),
    isConnecting: readonly(isConnecting),
    connectionError: readonly(connectionError),
    canConnect,

    // Actions
    connect,
    disconnect,
  }
}
