// fe/src/composables/useSocket.ts
// Unified WebSocket composable - Real-time communication with type safety

import { ref, computed, onUnmounted } from 'vue'
import type { WebSocketEvent } from '@/types/shared'
import { useAuthStore } from '@/stores/auth'

interface WebSocketMessage {
    type: string
    payload: Record<string, any>
}

interface SocketState {
    socket: WebSocket | null
    isConnected: boolean
    isConnecting: boolean
    retryCount: number
    listeners: Map<string, Set<Function>>
    householdId: number | null
    lastActivity: Date
}

const state = ref<SocketState>({
    socket: null,
    isConnected: false,
    isConnecting: false,
    retryCount: 0,
    listeners: new Map(),
    householdId: null,
    lastActivity: new Date()
})

const MAX_RETRY_ATTEMPTS = 5
const RETRY_DELAY_BASE = 1000 // 1 second
const HEARTBEAT_INTERVAL = 30000 // 30 seconds
const RECONNECT_INTERVAL = 5000 // 5 seconds

let heartbeatTimer: ReturnType<typeof setInterval> | null = null
let reconnectTimer: ReturnType<typeof setTimeout> | null = null

export function useSocket() {
    const authStore = useAuthStore()

    // Computed properties
    const isConnected = computed(() => state.value.isConnected)
    const isConnecting = computed(() => state.value.isConnecting)
    const connectionStatus = computed(() => {
        if (state.value.isConnected) return 'connected'
        if (state.value.isConnecting) return 'connecting'
        return 'disconnected'
    })

    // Connect to household WebSocket
    function connect(householdId: number, forceReconnect = false) {
        if (!authStore.accessToken || !authStore.user) {
            console.warn('[Socket] Cannot connect: No authentication')
            return Promise.reject(new Error('No authentication'))
        }

        if (state.value.isConnected && state.value.householdId === householdId && !forceReconnect) {
            console.log('[Socket] Already connected to household', householdId)
            return Promise.resolve()
        }

        if (state.value.isConnecting) {
            console.log('[Socket] Connection already in progress')
            return Promise.resolve()
        }

        disconnect() // Clean up existing connection

        state.value.isConnecting = true
        state.value.householdId = householdId

        return new Promise<void>((resolve, reject) => {
            try {
                const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
                const host = window.location.host
                const wsUrl = `${protocol}//${host}/api/v1/ws/household/${householdId}?token=${encodeURIComponent(authStore.accessToken || '')}`

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

                const socket = new WebSocket(wsUrl)

                socket.onopen = () => {
                    console.log('[Socket] Connected to household', householdId)
                    state.value.socket = socket
                    state.value.isConnected = true
                    state.value.isConnecting = false
                    state.value.retryCount = 0
                    state.value.lastActivity = new Date()

                    startHeartbeat()
                    resolve()
                }

                socket.onmessage = (event) => {
                    try {
                        const wsEvent: WebSocketEvent = JSON.parse(event.data)
                        handleMessage(wsEvent)
                    } catch (error) {
                        console.error('[Socket] Failed to parse message:', error, event.data)
                    }
                }

                socket.onerror = (error) => {
                    console.error('[Socket] WebSocket error:', error)
                    state.value.isConnecting = false
                    reject(error)
                }

                socket.onclose = (event) => {
                    console.log('[Socket] Disconnected:', event.code, event.reason)
                    cleanup()

                    // Auto-reconnect if not manually closed
                    if (event.code !== 1000 && state.value.retryCount < MAX_RETRY_ATTEMPTS) {
                        scheduleReconnect(householdId)
                    }
                }

            } catch (error) {
                console.error('[Socket] Connection failed:', error)
                state.value.isConnecting = false
                reject(error)
            }
        })
    }

    // Disconnect from WebSocket
    function disconnect() {
        if (state.value.socket) {
            state.value.socket.close(1000, 'Manual disconnect')
        }
        cleanup()
    }

    // Clean up connection state
    function cleanup() {
        state.value.socket = null
        state.value.isConnected = false
        state.value.isConnecting = false

        if (heartbeatTimer) {
            clearInterval(heartbeatTimer)
            heartbeatTimer = null
        }

        if (reconnectTimer) {
            clearTimeout(reconnectTimer)
            reconnectTimer = null
        }
    }

    // Start heartbeat to keep connection alive
    function startHeartbeat() {
        heartbeatTimer = setInterval(() => {
            if (state.value.isConnected && state.value.socket) {
                send({
                    type: 'presence_update',
                    payload: { action: 'heartbeat' }
                })
            }
        }, HEARTBEAT_INTERVAL)
    }

    // Schedule reconnection with exponential backoff
    function scheduleReconnect(householdId: number) {
        if (reconnectTimer) return

        const delay = RETRY_DELAY_BASE * Math.pow(2, state.value.retryCount)
        console.log(`[Socket] Scheduling reconnect in ${delay}ms (attempt ${state.value.retryCount + 1})`)

        reconnectTimer = setTimeout(() => {
            state.value.retryCount++
            reconnectTimer = null
            connect(householdId, true).catch(() => {
                // Failed to reconnect, will try again
            })
        }, delay)
    }

    // Handle incoming WebSocket messages
    function handleMessage(wsEvent: WebSocketEvent) {
        state.value.lastActivity = new Date()

        console.log('[Socket] Received event:', wsEvent.event, wsEvent.payload)

        // Handle system events
        if (wsEvent.event === 'connection:established') {
            console.log('[Socket] Connection established:', wsEvent.payload)
            return
        }

        if (wsEvent.event === 'ping' || wsEvent.event === 'list:ping') {
            // Respond to server ping
            send({ type: 'pong', payload: {} })
            return
        }

        // Emit event to registered listeners
        const listeners = state.value.listeners.get(wsEvent.event)
        if (listeners) {
            listeners.forEach(callback => {
                try {
                    callback(wsEvent.payload)
                } catch (error) {
                    console.error('[Socket] Error in event listener:', error)
                }
            })
        }
    }

    // Send message to server
    function send(message: WebSocketMessage) {
        if (!state.value.isConnected || !state.value.socket) {
            console.warn('[Socket] Cannot send message: Not connected')
            return false
        }

        try {
            state.value.socket.send(JSON.stringify(message))
            return true
        } catch (error) {
            console.error('[Socket] Failed to send message:', error)
            return false
        }
    }

    // Register event listener
    function on(event: string, callback: Function) {
        if (!state.value.listeners.has(event)) {
            state.value.listeners.set(event, new Set())
        }
        state.value.listeners.get(event)!.add(callback)
    }

    // Unregister event listener
    function off(event: string, callback: Function) {
        const listeners = state.value.listeners.get(event)
        if (listeners) {
            listeners.delete(callback)
            if (listeners.size === 0) {
                state.value.listeners.delete(event)
            }
        }
    }

    // Emit event (for client-side events)
    function emit(event: string, payload: any) {
        const message: WebSocketMessage = {
            type: 'custom_event',
            payload: { event, ...payload }
        }
        send(message)
    }

    // Indicate user is editing an entity
    function startEditing(entityType: string, entityId: number, field?: string) {
        send({
            type: 'editing_started',
            payload: { entity_type: entityType, entity_id: entityId, field }
        })
    }

    // Indicate user stopped editing an entity
    function stopEditing(entityType: string, entityId: number) {
        send({
            type: 'editing_stopped',
            payload: { entity_type: entityType, entity_id: entityId }
        })
    }

    // Update user presence/activity
    function updatePresence(action: string = 'active') {
        send({
            type: 'presence_update',
            payload: { action }
        })
    }

    // Get connection statistics
    function getStats() {
        return {
            isConnected: state.value.isConnected,
            isConnecting: state.value.isConnecting,
            householdId: state.value.householdId,
            retryCount: state.value.retryCount,
            lastActivity: state.value.lastActivity,
            listenerCount: Array.from(state.value.listeners.values()).reduce((sum, set) => sum + set.size, 0)
        }
    }

    // Clean up on component unmount
    onUnmounted(() => {
        disconnect()
    })

    return {
        // State
        isConnected,
        isConnecting,
        connectionStatus,

        // Methods
        connect,
        disconnect,
        send,
        on,
        off,
        emit,
        startEditing,
        stopEditing,
        updatePresence,
        getStats
    }
} 