<script setup>
import { ref, onMounted, onUnmounted, watch, computed } from 'vue'
import { SwitchCamera, Trash2, Copy, Download, X, QrCode } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import { toBase64Url } from '@gkucmierz/utils'
import { useCamera } from '../../composables/useCamera'
import { useQrDetection } from '../../composables/useQrDetection'

const scannedCodes = ref([])
const isFullscreen = ref(false)
const videoAspect = ref(1)
const wrapperRef = ref(null)
const bgCanvas = ref(null)
let bgRafId = null

const videoRef = ref(null)
const router = useRouter()

const navigateToGenerateQr = (text) => {
  const payload = toBase64Url(text)
  router.push({ name: 'QrCode', params: { payload } })
}
const overlayCanvas = ref(null)

const {
  stream,
  facingMode,
  hasMultipleCameras,
  isMirrored,
  error: cameraError,
  checkCameras,
  startCamera,
  stopCamera,
  switchCamera: baseSwitchCamera
} = useCamera(videoRef)

const {
  error: detectionError,
  isDetecting,
  startDetection,
  stopDetection
} = useQrDetection(videoRef, overlayCanvas)

const error = computed(() => cameraError.value || detectionError.value)

// Background Loop
const updateVideoAspect = () => {
  if (videoRef.value && videoRef.value.videoWidth && videoRef.value.videoHeight) {
    videoAspect.value = videoRef.value.videoWidth / videoRef.value.videoHeight
  }
}

const startBackgroundLoop = () => {
  const draw = () => {
    const videoEl = videoRef.value
    const canvas = bgCanvas.value
    if (!videoEl || !canvas || videoEl.paused || videoEl.ended) {
      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) {
      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)
}

const stopBackgroundLoop = () => {
  if (bgRafId) {
    cancelAnimationFrame(bgRafId)
    bgRafId = null
  }
}

// Full screen styles
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 (isFullscreen.value) {
    processCodes(detectedCodes)
    return
  }
  const videoEl = document.querySelector('.camera-wrapper video')
  if (!videoEl || !videoEl.videoWidth || !videoEl.videoHeight) {
    processCodes(detectedCodes)
    return
  }

  const { videoWidth, videoHeight } = videoEl
  const isLandscape = videoWidth > videoHeight
  let visibleX, visibleY, visibleW, visibleH

  if (isLandscape) {
    visibleH = videoHeight
    visibleW = videoHeight
    visibleX = (videoWidth - videoHeight) / 2
    visibleY = 0
  } else {
    visibleW = videoWidth
    visibleH = videoWidth
    visibleX = 0
    visibleY = (videoHeight - videoWidth) / 2
  }

  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 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 })

const handleKeydown = (e) => {
  if (e.key === 'Escape' && isFullscreen.value) {
    toggleFullscreen()
  }
}

const startScan = async () => {
  try {
    await startCamera()
    updateVideoAspect()
    startBackgroundLoop()
    startDetection(onDetect)
  } catch (err) {
    // Error is handled by error computed property
  }
}

onMounted(() => {
  checkCameras()
  loadHistory()
  window.addEventListener('resize', updateVideoAspect)
  window.addEventListener('resize', startBackgroundLoop)
  window.addEventListener('keydown', handleKeydown)

  watch(isFullscreen, (fs) => {
    if (fs) {
      startBackgroundLoop()
    } else {
      stopBackgroundLoop()
    }
  }, { immediate: true })

  startScan()
})

onUnmounted(() => {
  window.removeEventListener('resize', updateVideoAspect)
  window.removeEventListener('resize', startBackgroundLoop)
  window.removeEventListener('keydown', handleKeydown)
  stopDetection()
  stopCamera()
})

const switchCamera = (event) => {
  if (event) event.stopPropagation()
  baseSwitchCamera()
}

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 class="header-actions"></div>
      </div>

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

          <div
            class="camera-wrapper"
            :class="{ 'clickable': !isFullscreen, 'is-mirrored': isMirrored }"
            :style="desktopFullscreenStyle"
            ref="wrapperRef"
            @click="!isFullscreen && toggleFullscreen()"
          >
            <video
              ref="videoRef"
              class="camera-feed"
              :class="{ 'is-mirrored': isMirrored }"
              autoplay
              playsinline
              muted
            ></video>

            <canvas ref="overlayCanvas" class="scan-overlay-canvas" :class="{ 'is-mirrored': isMirrored }"></canvas>

            <div v-if="error" class="error-overlay">
              <p>{{ error }}</p>
              <button @click="startScan" class="retry-btn" v-ripple>Retry</button>
            </div>

            <button
              v-if="hasMultipleCameras"
              class="switch-camera-btn"
              @click.stop="switchCamera"
              v-tooltip="'Switch Camera'"
              v-ripple
            >
              <SwitchCamera size="24" />
            </button>
          </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" v-tooltip="'Copy All'" v-ripple>
                  <Copy size="18" />
                </button>
                <button class="icon-btn" @click="downloadJson" v-tooltip="'Download JSON'" v-ripple>
                  <Download size="18" />
                </button>
                <button class="icon-btn delete-btn" @click="clearHistory" v-tooltip="'Clear All'" v-ripple>
                  <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)" v-tooltip="'Copy'" v-ripple>
                    <Copy size="18" />
                  </button>
                  <button class="icon-btn" @click="navigateToGenerateQr(code.value)" v-tooltip="'Generate QR Code'" v-ripple>
                    <QrCode size="18" />
                  </button>
                  <button class="icon-btn delete-btn" @click="removeCode(code.id)" v-tooltip="'Remove'" v-ripple>
                    <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>

.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;
}

:global(:root[data-theme="light"] .scanner-content.is-fullscreen) {
  background: #fff;
}

.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;
}

:global(:root[data-theme="light"] .camera-wrapper) {
  background: #f1f5f9;
}

.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;
}

.camera-bg.is-mirrored {
  transform: scaleX(-1);
}

.camera-feed {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.camera-feed.is-mirrored {
  transform: scaleX(-1);
}

.scan-overlay-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 5;
}

.scan-overlay-canvas.is-mirrored {
  transform: scaleX(-1);
}

.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 !important;
  top: auto !important;
  left: auto !important;
  bottom: 0.75rem !important;
  right: 0.75rem !important;
  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);
}

.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 var(--glass-border);
  display: flex;
  flex-direction: column;
}

:global(:root[data-theme="light"] .scanner-content.is-fullscreen .results-section) {
  background: rgba(255, 255, 255, 0.75);
}

.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);
}

.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);
}

.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>
