<script setup lang="ts">
import { ref, nextTick, onMounted, onUnmounted } from 'vue'
import { useServerStore } from '@/stores/server'
import { useWebSocket, type WebSocketMessage } from '@/composables/useWebSocket'
import Panel from '@/components/ds/data/Panel.vue'
import ConsoleLine from '@/components/ds/data/ConsoleLine.vue'
import Button from '@/components/ds/core/Button.vue'
import Badge from '@/components/ds/core/Badge.vue'
import Icon from '@/components/ds/core/Icon.vue'
import Input from '@/components/ds/forms/Input.vue'

const server = useServerStore()
const ws = useWebSocket()

interface LogLine {
  timestamp: string
  text: string
  type: 'info' | 'warning' | 'error' | 'command' | 'system'
}

const lines = ref<LogLine[]>([])
const commandInput = ref('')
const consoleEl = ref<HTMLElement | null>(null)
const sending = ref(false)

function now(): string {
  return new Date().toLocaleTimeString('en-US', { hour12: false })
}

function addLine(text: string, type: LogLine['type'] = 'info') {
  lines.value.push({ timestamp: now(), text, type })
  scrollToBottom()
}

async function scrollToBottom() {
  await nextTick()
  if (consoleEl.value) {
    consoleEl.value.scrollTop = consoleEl.value.scrollHeight
  }
}

async function handleSend() {
  const cmd = commandInput.value.trim()
  if (!cmd || sending.value) return

  addLine(`> ${cmd}`, 'command')
  commandInput.value = ''
  sending.value = true

  try {
    const result = await server.sendCommand(cmd) as { output?: string }
    if (result?.output) {
      addLine(result.output, 'info')
    } else {
      addLine('Command sent.', 'system')
    }
  } catch {
    addLine('Failed to send command. Is the server connected?', 'error')
  } finally {
    sending.value = false
  }
}

function clearConsole() {
  lines.value = []
}

function lineLevel(type: LogLine['type']): 'cmd' | 'warn' | 'error' | 'info' {
  switch (type) {
    case 'command': return 'cmd'
    case 'warning': return 'warn'
    case 'error': return 'error'
    default: return 'info'
  }
}

function handleWebSocketMessage(message: WebSocketMessage) {
  if (message.type !== 'event') return
  if (message.event !== 'console_output') return

  const text = message.data?.line ?? message.data?.output ?? message.raw ?? ''
  if (text) {
    addLine(text, 'info')
  }
}

let unsubscribe: (() => void) | null = null

onMounted(() => {
  addLine('Catalyst Console initialized.', 'system')
  addLine('Type a command and press Enter to send it to the server.', 'system')
  if (server.connection?.connection_status !== 'connected') {
    addLine('Warning: server is not connected. Commands will fail.', 'warning')
  }
  unsubscribe = ws.subscribe(handleWebSocketMessage)
})

onUnmounted(() => {
  if (unsubscribe) {
    unsubscribe()
    unsubscribe = null
  }
})
</script>

<template>
  <div class="cv">
    <!-- Page head -->
    <div class="cv__head">
      <div class="cv__head-id">
        <div class="cv__head-chip">
          <Icon name="terminal" :size="20" :stroke-width="2" />
        </div>
        <div>
          <div class="t-eyebrow">Server management</div>
          <h1 class="cv__title">Console</h1>
        </div>
      </div>
      <div class="cv__head-actions">
        <Badge
          :tone="server.connection?.connection_status === 'connected' ? 'online' : 'offline'"
          :dot="true"
          :pulse="server.connection?.connection_status === 'connected'"
        >
          {{ server.connection?.connection_status === 'connected' ? 'Connected' : 'Disconnected' }}
        </Badge>
        <Button variant="ghost" size="sm" icon="trash-2" @click="clearConsole">Clear</Button>
      </div>
    </div>

    <!-- Console panel -->
    <Panel :flush-body="true" title="Output">
      <div ref="consoleEl" class="cv__output">
        <div v-if="lines.length === 0" class="cv__empty">
          No output yet. Send a command to get started.
        </div>
        <ConsoleLine
          v-for="(line, i) in lines"
          :key="i"
          :time="line.timestamp"
          :level="lineLevel(line.type)"
        >{{ line.text }}</ConsoleLine>
      </div>
      <div class="cv__bar">
        <span class="cv__bar-prompt">$</span>
        <Input
          v-model="commandInput"
          :mono="true"
          size="sm"
          placeholder="say Hello everyone…"
          :disabled="sending"
          style="flex: 1"
          @keydown.enter="handleSend"
        />
        <Button
          size="sm"
          variant="secondary"
          icon="corner-down-left"
          :loading="sending"
          :disabled="!commandInput.trim() || sending"
          @click="handleSend"
        >Send</Button>
      </div>
    </Panel>
  </div>
</template>

<style scoped>
.cv {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Page head */
.cv__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}
.cv__head-id { display: flex; align-items: center; gap: 12px; }
.cv__head-chip {
  width: 40px; height: 40px; flex: none; border-radius: var(--radius-md);
  display: flex; align-items: center; justify-content: center;
  color: var(--accent); background: var(--accent-soft);
  box-shadow: inset 0 0 0 1px var(--accent-border);
}
.cv__title {
  font-size: var(--text-2xl); font-weight: 700; letter-spacing: -0.02em;
  color: var(--text-primary); margin-top: 3px;
}
.cv__head-actions { display: flex; align-items: center; gap: 9px; }

/* Output area */
.cv__output {
  min-height: 420px;
  max-height: 600px;
  overflow-y: auto;
  padding: 8px 0;
  background: var(--surface-inset);
}
.cv__empty {
  padding: 16px 14px;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-style: italic;
}

/* Command bar */
.cv__bar {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 12px;
  border-top: 1px solid var(--border-subtle);
  background: var(--surface-base);
}
.cv__bar-prompt {
  font-family: var(--font-mono);
  color: var(--accent-text);
  font-weight: 700;
  font-size: var(--text-sm);
  flex: none;
}
</style>
