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

export interface FrameBBox {
  x: number
  y: number
  w: number
  h: number
  confidence: number
  label: string
  resolved_brand?: string | null
  source?: string | null
  stage?: string | null
  ocr_text?: string | null
}

export interface FrameOverlay {
  /** Base64 JPEG image (same dimensions as main image) */
  src: string
  label: string
  visible: boolean
  /** Opacity 0-1, default 0.5 */
  opacity?: number
}

const props = defineProps<{
  /** Base64 JPEG image */
  imageSrc: string
  /** Bounding boxes to overlay */
  boxes: FrameBBox[]
  /** Debug overlay layers (edge images, line visualizations, etc.) */
  overlays?: FrameOverlay[]
}>()

const canvas = ref<HTMLCanvasElement | null>(null)
const container = ref<HTMLElement | null>(null)

function draw() {
  const cvs = canvas.value
  const ctr = container.value
  if (!cvs || !ctr || !props.imageSrc) return

  const ctx = cvs.getContext('2d')
  if (!ctx) return

  const img = new window.Image()
  img.onload = () => {
    cvs.width = ctr.clientWidth
    cvs.height = ctr.clientHeight

    const scale = Math.min(cvs.width / img.width, cvs.height / img.height)
    const dx = (cvs.width - img.width * scale) / 2
    const dy = (cvs.height - img.height * scale) / 2

    ctx.clearRect(0, 0, cvs.width, cvs.height)
    ctx.drawImage(img, dx, dy, img.width * scale, img.height * scale)

    // Draw debug overlays (edge images, line visualizations)
    drawOverlays(ctx, dx, dy, img.width * scale, img.height * scale)

    // Draw bounding boxes on top
    for (const box of props.boxes) {
      const bx = dx + box.x * scale
      const by = dy + box.y * scale
      const bw = box.w * scale
      const bh = box.h * scale

      const color = sourceColor(box)
      const resolved = box.resolved_brand || box.ocr_text

      ctx.strokeStyle = color
      ctx.lineWidth = 2
      if (!resolved) {
        ctx.setLineDash([4, 3])
      }
      ctx.strokeRect(bx, by, bw, bh)
      ctx.setLineDash([])
    }
  }
  img.src = `data:image/jpeg;base64,${props.imageSrc}`
}

/** Pending overlay images that need async loading */
const overlayCache = new Map<string, HTMLImageElement>()

function drawOverlays(ctx: CanvasRenderingContext2D, dx: number, dy: number, dw: number, dh: number) {
  const layers = props.overlays ?? []
  for (const layer of layers) {
    if (!layer.visible || !layer.src) continue

    const cached = overlayCache.get(layer.src)
    if (cached && cached.complete) {
      ctx.globalAlpha = layer.opacity ?? 0.5
      ctx.drawImage(cached, dx, dy, dw, dh)
      ctx.globalAlpha = 1.0
    } else if (!cached) {
      // Load async, redraw when ready
      const overlay = new window.Image()
      overlay.onload = () => draw()
      overlay.src = `data:image/jpeg;base64,${layer.src}`
      overlayCache.set(layer.src, overlay)
    }
  }
}

const SOURCE_COLORS: Record<string, string> = {
  yolo: '#f5a623',       // yellow — raw detection
  ocr: '#ff8c42',        // orange — text extracted
  ocr_matched: '#3ecf8e', // green — brand resolved
  local_vlm: '#4f9cf9',  // blue — VLM resolved
  cloud_llm: '#a78bfa',  // purple — cloud resolved
  unresolved: '#e05252',  // red — nothing matched
}

// CV region labels — distinct from source-based colors
const REGION_COLORS: Record<string, string> = {
  edge_region: '#00bcd4',    // cyan
  contour_region: '#ffd54f', // yellow
  color_region: '#e040fb',   // magenta
  candidate: '#4caf50',      // green — passed readability
  rejected: '#e05252',       // red — failed readability
}

function sourceColor(box: FrameBBox): string {
  if (REGION_COLORS[box.label]) return REGION_COLORS[box.label]
  if (box.resolved_brand) return SOURCE_COLORS.ocr_matched
  if (box.source && SOURCE_COLORS[box.source]) return SOURCE_COLORS[box.source]
  return confidenceColor(box.confidence)
}

function confidenceColor(conf: number): string {
  if (conf >= 0.7) return 'var(--conf-high)'
  if (conf >= 0.4) return 'var(--conf-mid)'
  return 'var(--conf-low)'
}

watch(() => [props.imageSrc, props.boxes, props.overlays], () => nextTick(draw), { deep: true })

onMounted(() => {
  nextTick(draw)
  const observer = new ResizeObserver(() => draw())
  if (container.value) observer.observe(container.value)
  onUnmounted(() => observer.disconnect())
})
</script>

<template>
  <div ref="container" class="frame-renderer">
    <canvas ref="canvas" />
    <div v-if="!imageSrc" class="frame-empty">No frame</div>
  </div>
</template>

<style scoped>
.frame-renderer {
  width: 100%;
  height: 100%;
  min-height: 200px;
  position: relative;
}

.frame-renderer canvas {
  display: block;
  width: 100%;
  height: 100%;
}

.frame-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-dim);
}
</style>
