<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'

export interface LogEntry {
  level: string
  stage: string
  msg: string
  ts: string
}

const props = withDefaults(defineProps<{
  entries: LogEntry[]
  rowHeight?: number
  autoScroll?: boolean
}>(), {
  rowHeight: 24,
  autoScroll: true,
})

const container = ref<HTMLElement | null>(null)
const scrollTop = ref(0)
const containerHeight = ref(0)
const userScrolled = ref(false)

const visibleRange = computed(() => {
  const start = Math.floor(scrollTop.value / props.rowHeight)
  const visible = Math.ceil(containerHeight.value / props.rowHeight) + 2
  return {
    start: Math.max(0, start - 1),
    end: Math.min(props.entries.length, start + visible),
  }
})

const totalHeight = computed(() => props.entries.length * props.rowHeight)

const visibleEntries = computed(() =>
  props.entries.slice(visibleRange.value.start, visibleRange.value.end).map((entry, i) => ({
    ...entry,
    index: visibleRange.value.start + i,
  }))
)

function onScroll(e: Event) {
  const el = e.target as HTMLElement
  scrollTop.value = el.scrollTop
  // If user scrolled away from bottom, pause auto-scroll
  const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < props.rowHeight * 2
  userScrolled.value = !atBottom
}

function scrollToBottom() {
  if (container.value && props.autoScroll && !userScrolled.value) {
    container.value.scrollTop = container.value.scrollHeight
  }
}

watch(() => props.entries.length, () => {
  nextTick(scrollToBottom)
})

onMounted(() => {
  if (container.value) {
    containerHeight.value = container.value.clientHeight
    const observer = new ResizeObserver(([entry]) => {
      containerHeight.value = entry.contentRect.height
    })
    observer.observe(container.value)
    onUnmounted(() => observer.disconnect())
  }
})

const levelClass = (level: string) => level.toLowerCase()
</script>

<template>
  <div class="log-renderer" ref="container" @scroll="onScroll">
    <div class="log-spacer" :style="{ height: totalHeight + 'px' }">
      <div
        class="log-viewport"
        :style="{ transform: `translateY(${visibleRange.start * rowHeight}px)` }"
      >
        <div
          v-for="entry in visibleEntries"
          :key="entry.index"
          class="log-row"
          :class="levelClass(entry.level)"
          :style="{ height: rowHeight + 'px' }"
        >
          <span class="log-ts">{{ entry.ts }}</span>
          <span class="log-level">{{ entry.level }}</span>
          <span class="log-stage">{{ entry.stage }}</span>
          <span class="log-msg">{{ entry.msg }}</span>
        </div>
      </div>
    </div>
    <div v-if="entries.length === 0" class="log-empty">
      Waiting for log events...
    </div>
  </div>
</template>

<style scoped>
.log-renderer {
  overflow-y: auto;
  height: 100%;
  font-family: var(--font-mono);
  font-size: 12px;
}

.log-spacer {
  position: relative;
}

.log-viewport {
  position: absolute;
  left: 0;
  right: 0;
}

.log-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-2);
  line-height: 1;
}

.log-ts { color: var(--text-dim); min-width: 80px; flex-shrink: 0; }
.log-level { min-width: 56px; font-weight: 600; flex-shrink: 0; }
.log-stage { color: var(--status-processing); min-width: 120px; flex-shrink: 0; }
.log-msg { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.log-row.info .log-level { color: var(--status-live); }
.log-row.warning .log-level { color: var(--status-escalating); }
.log-row.error .log-level { color: var(--status-error); }
.log-row.debug .log-level { color: var(--text-dim); }

.log-empty {
  color: var(--text-dim);
  padding: var(--space-6);
  text-align: center;
}
</style>
