////
// The terminal communicates with the shell via websockets.

import type { Message } from "../shared/types"
import { sessionId } from "./session"
import { dispatchMessage } from "./dispatch"
import { addErrorMessage } from "./scrollback"

const MAX_RETRIES = 5
let retries = 0
let connected = false
let msgQueue: Omit<Message, "session">[] = []

let ws: WebSocket | null = null

// open our websocket connection
export function startConnection() {
    const url = new URL(`/ws?session=${sessionId}`, location.href)
    url.protocol = url.protocol.replace('http', 'ws')
    ws = new WebSocket(url)

    ws.onmessage = receive
    ws.onclose = retryConnection
    ws.onerror = () => ws?.close()
    ws.onopen = () => {
        connected = true
        msgQueue.forEach(msg => send(msg))
        msgQueue.length = 0
    }
}

// send any message
export function send(msg: Omit<Message, "session">) {
    if (!connected) {
        msgQueue.push(msg)
        startConnection()
        return
    }

    if (!(msg as any).session) (msg as any).session = sessionId

    ws?.readyState === 1 && ws.send(JSON.stringify(msg))
    console.log("-> send", msg)
}

async function receive(e: MessageEvent) {
    const data = JSON.parse(e.data) as Message
    console.log("<- receive", data)
    await dispatchMessage(data)
}

// close it... plz don't do this, though
export function close() {
    ws?.close(1000, 'bye')
}

function retryConnection() {
    connected = false

    if (retries >= MAX_RETRIES) {
        addErrorMessage(`Failed to reconnect ${retries} times. Server is down.`)
        if (ws) ws.onclose = () => { }
        return
    }
    retries++
    addErrorMessage(`Connection lost. Retrying...`)
    setTimeout(startConnection, 2000)
}
