<script setup>
import { ref, onMounted, onUnmounted, watch, computed } from 'vue'
import { QrcodeStream } from 'vue-qrcode-reader'
import { SwitchCamera, Trash2, Copy, Download, X } from 'lucide-vue-next'

const error = ref('')
const facingMode = ref('environment')
const scannedCodes = ref([])
const hasMultipleCameras = ref(false)
const isFullscreen = ref(false)
const videoAspect = ref(1)
const isFront = computed(() => facingMode.value === 'user')
const wrapperRef = ref(null)
let frontMirrorObserver = null
const bgCanvas = ref(null)
let bgRafId = null
const updateVideoAspect = () => {
  const videoEl = document.querySelector('.camera-wrapper video')
  if (videoEl && videoEl.videoWidth && videoEl.videoHeight) {
    videoAspect.value = videoEl.videoWidth / videoEl.videoHeight
  }
}
const startBackgroundLoop = () => {
  const draw = () => {
    const videoEl = document.querySelector('.camera-wrapper video')
    const canvas = bgCanvas.value
    if (!videoEl || !canvas) {
      bgRafId = requestAnimationFrame(draw)
      return
    }
    const vw = videoEl.videoWidth || 0
    const vh = videoEl.videoHeight || 0
    if (!vw || !vh) {
      bgRafId = requestAnimationFrame(draw)
      return
    }
    const cw = Math.floor(window.innerWidth)
    const ch = Math.floor(window.innerHeight * 0.5)
    if (canvas.width !== cw || canvas.height !== ch) {
      canvas.width = cw
      canvas.height = ch
    }
    const ctx = canvas.getContext('2d')
    if (ctx) {
      // cover horizontally: scale by width, crop top/bottom
      const scale = cw / vw
      const srcH = ch / scale
      const sx = 0
      const sy = Math.max(0, (vh - srcH) / 2)
      ctx.clearRect(0, 0, cw, ch)
      ctx.drawImage(videoEl, sx, sy, vw, srcH, 0, 0, cw, ch)
    }
    bgRafId = requestAnimationFrame(draw)
  }
  if (bgRafId) cancelAnimationFrame(bgRafId)
  bgRafId = requestAnimationFrame(draw)
}

// front mirror canvas removed to restore stable behavior
const stopBackgroundLoop = () => {
  if (bgRafId) {
    cancelAnimationFrame(bgRafId)
    bgRafId = null
  }
}
const desktopFullscreenStyle = computed(() => {
  if (!isFullscreen.value) return {}
  const isDesktop = window.innerWidth >= 768
  if (!isDesktop) return {}
  const halfHeight = Math.floor(window.innerHeight * 0.5)
  const widthPx = Math.min(window.innerWidth, Math.floor(halfHeight * videoAspect.value))
  return { height: `${halfHeight}px`, width: `${widthPx}px`, margin: '0 auto' }
})

const processCodes = (codes) => {
  codes.forEach(code => {
    const value = code.rawValue
    if (value && !scannedCodes.value.some(c => c.value === value)) {
      scannedCodes.value.unshift({
        id: Date.now() + Math.random(),
        value,
        format: code.format,
        timestamp: new Date().toLocaleTimeString()
      })
    }
  })
}

const onDetect = (detectedCodes) => {
  // If fullscreen, accept all detected codes (as the user sees the full camera view mostly)
  if (isFullscreen.value) {
    processCodes(detectedCodes)
    return
  }

  // Try to find video element to calculate visible area
  const videoEl = document.querySelector('.camera-wrapper video')

  if (!videoEl || !videoEl.videoWidth || !videoEl.videoHeight) {
    processCodes(detectedCodes)
    return
  }

  const { videoWidth, videoHeight } = videoEl

  // Calculate visible square area (assuming object-fit: cover and 1:1 container)
  const isLandscape = videoWidth > videoHeight
  let visibleX, visibleY, visibleW, visibleH

  if (isLandscape) {
    // Landscape: sides are cropped, height is fully visible
    visibleH = videoHeight
    visibleW = videoHeight // Square
    visibleX = (videoWidth - videoHeight) / 2
    visibleY = 0
  } else {
    // Portrait: top/bottom are cropped, width is fully visible
    visibleW = videoWidth
    visibleH = videoWidth // Square
    visibleX = 0
    visibleY = (videoHeight - videoWidth) / 2
  }

  // Add margin to be safe (code center must be within visible area)
  // We allow codes slightly outside if their center is inside
  const validCodes = detectedCodes.filter(code => {
    if (!code.boundingBox) return true
    const { x, y, width, height } = code.boundingBox
    const centerX = x + width / 2
    const centerY = y + height / 2

    return (
      centerX >= visibleX &&
      centerX <= visibleX + visibleW &&
      centerY >= visibleY &&
      centerY <= visibleY + visibleH
    )
  })

  processCodes(validCodes)
}

const onCameraOn = async (capabilities) => {
  // Camera is ready
  setTimeout(updateVideoAspect, 100)
  setTimeout(startBackgroundLoop, 150)
  // Flip is handled via global CSS; no JS flips needed
}

const ensureFrontMirror = () => {
  // No-op: mirror is applied via CSS selectors
}

const startFrontMirrorObserver = () => {
  // No-op: mirror is applied via CSS selectors
}

const stopFrontMirrorObserver = () => {
  // No-op
}

watch(isFront, () => {
  // CSS-based; nothing to do
})

const paintDetections = (detectedCodes, ctx) => {
  try {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
    const styles = getComputedStyle(document.documentElement)
    const accent = styles.getPropertyValue('--primary-accent').trim() || '#00f2fe'
    detectedCodes.forEach(code => {
      if (code.format && code.format !== 'qr_code') return
      const points = code.cornerPoints || []
      if (points.length < 4) return
      ctx.beginPath()
      ctx.moveTo(points[0].x, points[0].y)
      for (let i = 1; i < points.length; i++) {
        ctx.lineTo(points[i].x, points[i].y)
      }
      ctx.closePath()
      ctx.lineWidth = 3
      ctx.strokeStyle = accent
      ctx.shadowColor = accent
      ctx.shadowBlur = 8
      ctx.stroke()
      ctx.shadowBlur = 0
      ctx.fillStyle = accent
      points.forEach(p => {
        ctx.beginPath()
        ctx.arc(p.x, p.y, 2.5, 0, Math.PI * 2)
        ctx.fill()
      })
    })
  } catch (e) {
    // ignore drawing errors
  }
}
const onError = (err) => {
  if (err.name === 'NotAllowedError') {
    error.value = 'Camera permission denied'
  } else if (err.name === 'NotFoundError') {
    error.value = 'No camera found'
  } else {
    error.value = `Camera error: ${err.name}`
  }
}

const checkCameras = async () => {
  try {
    if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
      return
    }
    const devices = await navigator.mediaDevices.enumerateDevices()
    const cameras = devices.filter(d => d.kind === 'videoinput')
    hasMultipleCameras.value = cameras.length > 1
  } catch (e) {
    console.error('Error checking cameras:', e)
  }
}

const loadHistory = () => {
  try {
    const saved = localStorage.getItem('qr-history')
    if (saved) {
      scannedCodes.value = JSON.parse(saved)
    }
  } catch (e) {
    console.error('Failed to load QR history', e)
  }
}

watch(scannedCodes, (newVal) => {
  localStorage.setItem('qr-history', JSON.stringify(newVal))
}, { deep: true })

onMounted(() => {
  checkCameras()
  loadHistory()
  window.addEventListener('resize', updateVideoAspect)
  window.addEventListener('resize', startBackgroundLoop)
  watch(isFullscreen, (fs) => {
    if (fs) {
      startBackgroundLoop()
    } else {
      stopBackgroundLoop()
    }
  }, { immediate: true })
})

onUnmounted(() => {
  window.removeEventListener('resize', updateVideoAspect)
  window.removeEventListener('resize', startBackgroundLoop)
  stopBackgroundLoop()
})

const switchCamera = (event) => {
  event.stopPropagation()
  facingMode.value = facingMode.value === 'environment' ? 'user' : 'environment'
}

const toggleFullscreen = () => {
  isFullscreen.value = !isFullscreen.value
}

const clearHistory = () => {
  scannedCodes.value = []
}

const removeCode = (id) => {
  scannedCodes.value = scannedCodes.value.filter(c => c.id !== id)
}

const copyAll = async () => {
  if (scannedCodes.value.length === 0) return
  const text = scannedCodes.value.map(c => c.value).join('\n')
  try {
    await navigator.clipboard.writeText(text)
  } catch (err) {
    console.error('Failed to copy', err)
  }
}

const downloadJson = () => {
  if (scannedCodes.value.length === 0) return
  const data = JSON.stringify(scannedCodes.value, null, 2)
  const blob = new Blob([data], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `qr-scan-history-${new Date().toISOString().slice(0, 10)}.json`
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
  URL.revokeObjectURL(url)
}

const copyToClipboard = async (text) => {
  try {
    await navigator.clipboard.writeText(text)
  } catch (err) {
    console.error('Failed to copy', err)
  }
}

const isUrl = (string) => {
  try {
    return Boolean(new URL(string))
  } catch (_) {
    return false
  }
}
</script>

<template>
  <div class="tool-container full-width">
    <div class="tool-panel">
      <div class="panel-header" v-if="!isFullscreen">
        <h2 class="tool-title">QR Scanner</h2>
      </div>

      <Teleport to="body" :disabled="!isFullscreen">
        <div class="scanner-content" :class="{ 'is-fullscreen': isFullscreen }">
          <canvas
            v-if="isFullscreen"
            ref="bgCanvas"
            class="camera-bg"
          ></canvas>
          <button v-if="isFullscreen" class="close-fullscreen-btn" @click="toggleFullscreen">
            <X size="24" />
          </button>

          <div
            class="camera-wrapper"
            :class="{ 'clickable': !isFullscreen, 'is-front': isFront }"
            :style="desktopFullscreenStyle"
            ref="wrapperRef"
            @click="!isFullscreen && toggleFullscreen()"
          >
            <QrcodeStream
              :constraints="{ facingMode }"
              @detect="onDetect"
              @error="onError"
              @camera-on="onCameraOn"
              :track="paintDetections"
            >
              <div v-if="error" class="error-overlay">
                <p>{{ error }}</p>
              </div>

              <button
                v-if="hasMultipleCameras"
                class="switch-camera-btn"
                @click.stop="switchCamera"
                title="Switch Camera"
              >
                <SwitchCamera size="24" />
              </button>
            </QrcodeStream>
          </div>

          <div class="results-section">
            <div class="results-header">
              <h3>Scanned Codes ({{ scannedCodes.length }})</h3>
              <div v-if="scannedCodes.length > 0" class="header-actions">
                <button class="icon-btn" @click="copyAll" title="Copy All">
                  <Copy size="18" />
                </button>
                <button class="icon-btn" @click="downloadJson" title="Download JSON">
                  <Download size="18" />
                </button>
                <button class="icon-btn delete-btn" @click="clearHistory" title="Clear All">
                  <Trash2 size="18" />
                </button>
              </div>
            </div>

            <div v-if="scannedCodes.length > 0" class="codes-list">
              <div v-for="code in scannedCodes" :key="code.id" class="code-item">
                <div class="code-content">
                  <div class="code-value">
                    <a v-if="isUrl(code.value)" :href="code.value" target="_blank" rel="noopener noreferrer">{{ code.value }}</a>
                    <span v-else>{{ code.value }}</span>
                  </div>
                  <div class="code-meta">
                    <span class="timestamp">{{ code.timestamp }}</span>
                    <span class="format-badge">{{ code.format }}</span>
                  </div>
                </div>
                <div class="item-actions">
                  <button class="icon-btn" @click="copyToClipboard(code.value)" title="Copy">
                    <Copy size="18" />
                  </button>
                  <button class="icon-btn delete-btn" @click="removeCode(code.id)" title="Remove">
                    <Trash2 size="18" />
                  </button>
                </div>
              </div>
            </div>
            <div v-else class="empty-state">
              Point camera at a QR code to scan
            </div>
          </div>
        </div>
      </Teleport>
    </div>
  </div>
</template>

<style scoped>
.tool-container.full-width {
  max-width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.tool-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  gap: 1.5rem;
  padding: 1.5rem;
}

.panel-header {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-bottom: 0.5rem;
  margin-top: 0;
  position: relative;
  width: 100%;
}

.scanner-content {
  display: flex;
  flex-direction: column;
  height: 100%;
  gap: 1.5rem;
}

.scanner-content.is-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #000;
  gap: 0;
}

.camera-wrapper {
  width: 100%;
  max-width: 500px;
  aspect-ratio: 1;
  margin: 0 auto;
  border-radius: 12px;
  overflow: hidden;
  position: relative;
  background: #000;
  border: 1px solid var(--glass-border);
  box-shadow: var(--glass-shadow);
  transition: all 0.3s ease;
}

.camera-wrapper.clickable {
  cursor: pointer;
}

.scanner-content.is-fullscreen .camera-wrapper {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  max-width: none;
  height: auto;
  border-radius: 0;
  border: none;
  margin: 0;
  z-index: 1;
}

.camera-bg {
  position: absolute;
  left: 0;
  top: 0;
  width: 100vw;
  height: 50vh;
  filter: blur(16px) saturate(110%);
  opacity: 0.9;
  z-index: 0;
}

/* front mirror canvas removed */

.error-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.8);
  color: #ff4444;
  padding: 1rem;
  text-align: center;
  z-index: 10;
}

.switch-camera-btn {
  position: absolute;
  top: 1rem;
  right: 1rem;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: #fff;
  border-radius: 50%;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 20;
  backdrop-filter: blur(4px);
  transition: all 0.2s;
}

.switch-camera-btn:hover {
  background: rgba(0, 0, 0, 0.6);
  transform: scale(1.05);
}

.close-fullscreen-btn {
  position: absolute;
  top: 1rem;
  left: 1rem;
  z-index: 20;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid rgba(255, 255, 255, 0.2);
  color: #fff;
  border-radius: 50%;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  backdrop-filter: blur(4px);
}

/* Removed legacy scan frame overlay - using shape detection rendering via track instead */

.results-section {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: 12px;
}

.scanner-content.is-fullscreen .results-section {
  position: relative;
  flex: 1;
  width: 100%;
  height: auto;
  z-index: 2;
  border-radius: 0;
  background: var(--glass-bg);
  backdrop-filter: blur(10px);
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.2);
  display: flex;
  flex-direction: column;
}

.results-header {
  padding: 1rem;
  border-bottom: 1px solid var(--glass-border);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.results-header h3 {
  margin: 0;
  font-size: 1.1rem;
  color: var(--text-strong);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.codes-list {
  flex: 1;
  overflow-y: auto;
  padding: 0;
}

.code-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem;
  border-bottom: 1px solid var(--glass-border);
  transition: background 0.2s;
}

.code-item:last-child {
  border-bottom: none;
}

.code-item:hover {
  background: var(--list-hover-bg);
}

.code-content {
  flex: 1;
  overflow: hidden;
  padding-right: 1rem;
}

.code-value {
  color: var(--primary-accent);
  font-family: monospace;
  word-break: break-all;
  margin-bottom: 0.4rem;
}

.code-value a {
  color: var(--primary-accent);
  text-decoration: underline;
}

.code-value a:hover {
  opacity: 0.8;
}

.code-meta {
  display: flex;
  gap: 1rem;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.item-actions {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

.format-badge {
  background: rgba(255, 255, 255, 0.1);
  padding: 0 0.4rem;
  border-radius: 4px;
  font-size: 0.75rem;
}

:global(:root[data-theme="light"]) .format-badge {
  background: rgba(0, 0, 0, 0.1);
}

.icon-btn {
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 0.4rem;
  border-radius: 4px;
  transition: all 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}

.icon-btn:hover {
  color: var(--text-color);
  background: rgba(255, 255, 255, 0.1);
}

:global(:root[data-theme="light"]) .icon-btn:hover {
  background: rgba(0, 0, 0, 0.05);
}

.delete-btn:hover {
  color: #ef4444;
}

.empty-state {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  color: var(--text-secondary);
  padding: 2rem;
  text-align: center;
}

:deep(video) {
  object-fit: cover !important;
  width: 100% !important;
  height: 100% !important;
  position: absolute !important;
  left: 0 !important;
  top: 0 !important;
}

:deep(.qrcode-stream-wrapper),
:deep(.qrcode-stream-overlay) {
  width: 100% !important;
  height: 100% !important;
  position: absolute !important;
  inset: 0 !important;
}

/* Front camera mirror (CSS-only) */
.camera-wrapper.is-front :deep(video) {
  transform: scaleX(-1);
  transform-origin: center;
}
.camera-wrapper.is-front :deep(#qrcode-stream-pause-frame),
.camera-wrapper.is-front :deep(#qrcode-stream-overlay) {
  transform: scaleX(-1);
  transform-origin: center;
}

@media (min-width: 768px) {
  :deep(.scanner-content.is-fullscreen .camera-wrapper video) {
    object-fit: contain !important;
  }
}
</style>
