import { buttonResults } from '../resources/vue/stores/buttonResults.js'
import { rateLimits } from '../resources/vue/stores/rateLimits.js'
import { connectionState } from '../resources/vue/stores/connectionState.js'

const RECONNECT_DELAY_MS = 10000

export function initWebsocket () {
  window.addEventListener('EventOutputChunk', onOutputChunk)
  window.addEventListener('EventExecutionStarted', onExecutionChanged)
  window.addEventListener('EventExecutionFinished', onExecutionChanged)

  reconnectWebsocket()
}

window.websocketAvailable = false

async function reconnectWebsocket () {
  if (window.websocketAvailable) {
    return
  }

  connectionState.reconnecting = true
  connectionState.connected = false
  if (connectionState.disconnectedAt == null) {
    connectionState.disconnectedAt = Date.now()
  }
  connectionState.nextReconnectAt = null

  try {
    window.websocketAvailable = true
    const stream = window.client.eventStream()
    connectionState.connected = true
    connectionState.reconnecting = false
    connectionState.nextReconnectAt = null
    for await (const e of stream) {
      connectionState.disconnectedAt = null
      handleEvent(e)
    }
  } catch (err) {
    console.error('Websocket connection failed: ', err)
  }

  window.websocketAvailable = false
  connectionState.connected = false
  connectionState.disconnectedAt = connectionState.disconnectedAt ?? Date.now()
  connectionState.nextReconnectAt = Date.now() + RECONNECT_DELAY_MS
  console.log('Reconnecting websocket in ' + RECONNECT_DELAY_MS + 'ms...')
  setTimeout(() => {
    reconnectWebsocket()
  }, RECONNECT_DELAY_MS)
}

async function refreshInitAfterConfigChange () {
  if (!window.client) {
    return
  }

  try {
    window.initResponse = await window.client.init({})

    if (typeof window.updateHeaderFromInit === 'function') {
      window.updateHeaderFromInit()
    }
  } catch (err) {
    console.error('Failed to refresh config from server after EventConfigChanged:', err)
  }
}

async function handleConfigChangedEvent (j) {
  await refreshInitAfterConfigChange()
  window.dispatchEvent(j)
}

function handleEvent (msg) {
  const typeName = msg.event.value.$typeName.replace('olivetin.api.v1.', '')

  const j = new Event(typeName)
  j.payload = msg.event.value

  switch (typeName) {
    case 'EventConfigChanged':
      handleConfigChangedEvent(j).catch((err) => {
        console.error('EventConfigChanged handler failed:', err)
      })
      break
    case 'EventOutputChunk':
    case 'EventEntityChanged':
      window.dispatchEvent(j)
      break
    case 'EventExecutionFinished':
    case 'EventExecutionStarted':
      window.dispatchEvent(j)
      break
    default:
      console.warn('Unhandled websocket message type from server: ', typeName)

      window.showBigError('ws-unhandled-message', 'handling websocket message', 'Unhandled websocket message type from server: ' + typeName, true)
  }
}

function onOutputChunk (evt) {
  const chunk = evt.payload

  if (window.terminal) {
    if (chunk.executionTrackingId === window.terminal.executionTrackingId) {
      window.terminal.write(chunk.output)
    }
  }
}

function onExecutionChanged (evt) {
  buttonResults[evt.payload.logEntry.executionTrackingId] = evt.payload.logEntry

  const logEntry = evt.payload.logEntry

  // Update rate limit store from logEntry if rate limit expiry datetime is provided
  if (logEntry && logEntry.datetimeRateLimitExpires && logEntry.bindingId) {
    // Parse datetime string "2006-01-02 15:04:05" and convert to Unix timestamp
    const date = new Date(logEntry.datetimeRateLimitExpires.replace(' ', 'T') + 'Z')
    rateLimits[logEntry.bindingId] = date.getTime() / 1000
  } else if (logEntry && logEntry.bindingId) {
    // Clear rate limit if not set
    rateLimits[logEntry.bindingId] = 0
  }
}
