<template>
  <div class="ocr-upload-zone">
    <div
      class="upload-dropzone"
      :class="{ 'dragging': isDragging, 'processing': processing }"
      @dragover.prevent="onDragOver"
      @dragleave.prevent="onDragLeave"
      @drop.prevent="onDrop"
      @click="triggerFileInput"
    >
      <input
        ref="fileInput"
        type="file"
        accept="image/*,application/pdf"
        class="hidden-input"
        @change="onFileSelected"
      />

      <div v-if="processing" class="processing-state">
        <ProgressSpinner
          style="width: 50px; height: 50px"
          strokeWidth="4"
        />
        <p class="processing-text">{{ processingMessage }}</p>
        <p class="processing-subtext">{{ processingSubtext }}</p>
      </div>

      <div v-else-if="selectedFile" class="file-selected-state">
        <i class="pi pi-check-circle" style="font-size: 1.75rem; color: var(--green-500);"></i>
        <p class="file-name">{{ selectedFile.name }}</p>
        <p class="file-size">{{ formatFileSize(selectedFile.size) }}</p>
      </div>

      <div v-else class="empty-state">
        <i class="pi pi-camera" style="font-size: 2rem; color: var(--primary-500);"></i>
        <p class="main-text">
          <span v-if="isDragging">Elibereaza pentru a incarca</span>
          <span v-else>Trage poza bonului aici sau click pentru a selecta</span>
        </p>
        <p class="sub-text">
          JPG, PNG, PDF (max 10MB) &bull; OCR extrage automat datele
        </p>
      </div>
    </div>

    <!-- Action Buttons (below dropzone) -->
    <div v-if="selectedFile && !processing" class="action-buttons">
      <Button
        label="Schimba"
        icon="pi pi-refresh"
        severity="secondary"
        size="small"
        @click="triggerFileInput"
      />
      <!-- OCR Engine Selector -->
      <Dropdown
        v-model="selectedEngine"
        :options="engineOptions"
        optionLabel="label"
        optionValue="value"
        placeholder="Motor OCR"
        class="engine-selector dropdown-borderless"
      >
        <template #option="{ option }">
          <div class="engine-option">
            <span class="engine-label">{{ option.label }}</span>
            <span class="engine-desc">{{ option.desc }}</span>
          </div>
        </template>
      </Dropdown>
      <Button
        label="Proceseaza OCR"
        icon="pi pi-cog"
        size="small"
        @click="processOCR"
      />
    </div>

    <!-- OCR Error Message -->
    <Message v-if="error" severity="error" :closable="true" @close="error = null">
      {{ error }}
    </Message>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import Dropdown from 'primevue/dropdown'
import api from '@data-entry/services/api'
import { useOCRSettingsStore } from '@data-entry/stores/ocrSettingsStore'

const emit = defineEmits(['ocr-result', 'file-selected', 'error'])

const fileInput = ref(null)
const selectedFile = ref(null)
const isDragging = ref(false)
const processing = ref(false)
const error = ref(null)

// OCR Settings Store - manages user preferences
const ocrStore = useOCRSettingsStore()

// OCR Engine selection (synced with store)
const selectedEngine = ref('auto')

// Engine config - labels and descriptions for dropdown
const engineConfig = {
  'auto': {
    label: 'Auto',
    desc: 'docTR→Paddle→Tess · General'
  },
  'doctr': {
    label: 'docTR',
    desc: 'Rapid, bună acuratețe'
  },
  'doctr_plus': {
    label: 'docTR Plus',
    desc: '2 treceri secvențiale · (recomandat)'
  },
  'paddleocr': {
    label: 'PaddleOCR',
    desc: 'Cea mai bună calitate'
  },
  'tesseract': {
    label: 'Tesseract',
    desc: 'Cel mai rapid, calitate redusă'
  },
  'hybrid': {
    label: 'Hybrid',
    desc: 'docTR+Tess paralel'
  },
  'hybrid-quality': {
    label: 'Hybrid Calitate',
    desc: 'Paddle→docTR→Tess · Acuratețe max'
  },
}

// Compute engine options from store's available engines
const engineOptions = computed(() => {
  return ocrStore.availableEngines.map(engine => ({
    label: engineConfig[engine]?.label || engine,
    desc: engineConfig[engine]?.desc || '',
    value: engine
  }))
})

// Load user's preferred engine on mount
onMounted(async () => {
  await ocrStore.loadPreference()
  selectedEngine.value = ocrStore.preferredEngine
  console.log('[OCRUploadZone] Loaded user preference:', selectedEngine.value)
})

// Save preference when user changes engine
watch(selectedEngine, async (newEngine, oldEngine) => {
  if (oldEngine && newEngine !== oldEngine && ocrStore.initialized) {
    try {
      await ocrStore.setPreference(newEngine)
      console.log('[OCRUploadZone] Saved user preference:', newEngine)
    } catch (err) {
      console.error('[OCRUploadZone] Failed to save preference:', err)
    }
  }
})

// Job queue state
const jobId = ref(null)
const queuePosition = ref(null)
const estimatedWait = ref(null)
const jobStatus = ref(null)

// Dynamic processing messages
const processingMessage = computed(() => {
  if (jobStatus.value === 'pending' && queuePosition.value > 0) {
    return `In coada... Pozitia ${queuePosition.value}`
  }
  if (jobStatus.value === 'processing') {
    return 'Se proceseaza imaginea...'
  }
  return 'Se trimite...'
})

const processingSubtext = computed(() => {
  if (jobStatus.value === 'pending' && estimatedWait.value > 0) {
    return `Timp estimat: ~${estimatedWait.value} secunde`
  }
  if (jobStatus.value === 'processing') {
    return 'Extragere date cu OCR'
  }
  return 'Asteptati...'
})

const onDragOver = () => {
  isDragging.value = true
}

const onDragLeave = () => {
  isDragging.value = false
}

const onDrop = (event) => {
  isDragging.value = false
  const files = event.dataTransfer?.files
  if (files?.length > 0) {
    handleFile(files[0])
  }
}

const triggerFileInput = () => {
  fileInput.value?.click()
}

const onFileSelected = (event) => {
  const files = event.target?.files
  if (files?.length > 0) {
    handleFile(files[0])
  }
}

const handleFile = async (file) => {
  // Validate file type
  const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf']
  if (!allowedTypes.includes(file.type)) {
    error.value = 'Tip de fisier invalid. Sunt acceptate doar: JPG, PNG, PDF'
    return
  }

  // Validate file size (10MB)
  if (file.size > 10 * 1024 * 1024) {
    error.value = 'Fisierul este prea mare. Dimensiunea maxima este 10MB.'
    return
  }

  error.value = null

  // FIX pentru Android: Clonăm fișierul în memorie IMEDIAT după selectare
  // pentru a evita invalidarea referinței de către browser (SnapshotState issue)
  // See: https://issues.chromium.org/40703873
  try {
    const arrayBuffer = await file.arrayBuffer()
    const clonedFile = new File([arrayBuffer], file.name, {
      type: file.type,
      lastModified: file.lastModified
    })
    selectedFile.value = clonedFile
    emit('file-selected', clonedFile)
  } catch (e) {
    // Fallback pentru browsere care nu suportă arrayBuffer
    console.warn('File clone failed, using original:', e)
    selectedFile.value = file
    emit('file-selected', file)
  }
}

const processOCR = async () => {
  if (!selectedFile.value) return

  processing.value = true
  error.value = null
  jobId.value = null
  queuePosition.value = null
  estimatedWait.value = null
  jobStatus.value = 'submitting'

  try {
    const formData = new FormData()
    formData.append('file', selectedFile.value)

    console.log('🔍 OCR Submit:', {
      fileName: selectedFile.value?.name,
      fileSize: selectedFile.value?.size,
      fileType: selectedFile.value?.type,
      engine: selectedEngine.value
    })

    // Step 1: Submit job to queue (returns immediately)
    // Include engine parameter in query string
    const submitResponse = await api.post(`/ocr/extract?engine=${selectedEngine.value}`, formData, {
      timeout: 30000, // 30s timeout for submission
    })

    const job = submitResponse.data
    jobId.value = job.job_id
    queuePosition.value = job.queue_position
    estimatedWait.value = job.estimated_wait_seconds
    jobStatus.value = job.status

    console.log('📋 OCR Job Created:', job)

    // Step 2: Start polling for result
    await pollJobStatus(job.job_id)

  } catch (err) {
    console.error('🔴 OCR Error:', {
      message: err.message,
      code: err.code,
      response: err.response?.data
    })
    const message = err.response?.data?.detail || err.message || 'Eroare la procesarea OCR'
    error.value = message
    emit('error', message)
    processing.value = false
  }
}

const pollJobStatus = async (id) => {
  const LONG_POLL_TIMEOUT = 30  // seconds
  const MAX_TOTAL_TIME = 120    // 2 minutes max
  const MIN_POLL_INTERVAL = 500  // Minimum 500ms between polls (safety net, backend does long-poll)
  const startTime = Date.now()

  const poll = async () => {
    // Check if exceeded max total time
    const elapsed = (Date.now() - startTime) / 1000
    if (elapsed >= MAX_TOTAL_TIME) {
      processing.value = false
      error.value = 'Timeout - procesarea a durat prea mult'
      emit('error', error.value)
      return
    }

    const pollStartTime = Date.now()

    try {
      // Long-poll with 30s server timeout, 35s axios timeout
      // Add cache-busting param to prevent browser/IIS caching
      const response = await api.get(`/ocr/jobs/${id}/wait`, {
        params: { timeout: LONG_POLL_TIMEOUT, _t: Date.now() },
        timeout: (LONG_POLL_TIMEOUT + 5) * 1000,
        headers: { 'Cache-Control': 'no-cache' }
      })

      const job = response.data
      jobStatus.value = job.status
      queuePosition.value = job.queue_position
      estimatedWait.value = job.estimated_wait_seconds

      const pollDuration = Date.now() - pollStartTime
      console.log('📊 OCR Long-Poll:', {
        status: job.status,
        position: job.queue_position,
        pollDurationMs: pollDuration
      })

      if (job.status === 'completed') {
        processing.value = false
        if (job.result) {
          console.log('✅ OCR Complete:', job.result)
          emit('ocr-result', {
            ...job.result,
            _processing_time_ms: job.processing_time_ms
          })
        } else {
          error.value = 'OCR completed but no result returned'
          emit('error', error.value)
        }
        return
      }

      if (job.status === 'failed') {
        processing.value = false
        error.value = job.error || 'OCR processing failed'
        emit('error', error.value)
        return
      }

      // Still pending/processing - long-poll again
      if (processing.value) {
        // Prevent rapid polling: if response came back too fast, wait before next poll
        const pollDuration = Date.now() - pollStartTime
        if (pollDuration < MIN_POLL_INTERVAL) {
          const waitTime = MIN_POLL_INTERVAL - pollDuration
          console.log(`⏳ Waiting ${waitTime}ms before next poll (preventing rapid polling)`)
          await new Promise(resolve => setTimeout(resolve, waitTime))
        }
        await poll()
      }

    } catch (err) {
      // Handle timeout (normal for long-poll)
      if (err.code === 'ECONNABORTED' || err.message?.includes('timeout')) {
        console.log('⏱️ Long-poll timeout, retrying...')
        if (processing.value) {
          await poll()
        }
        return
      }

      // Real error - wait before retry to prevent rapid error loops
      console.error('🔴 Poll Error:', err.message)

      // Check if we should retry or give up
      const elapsed = (Date.now() - startTime) / 1000
      if (elapsed < MAX_TOTAL_TIME && processing.value) {
        console.log('🔄 Retrying after error...')
        await new Promise(resolve => setTimeout(resolve, MIN_POLL_INTERVAL))
        await poll()
        return
      }

      processing.value = false
      error.value = 'Eroare la verificarea starii job-ului'
      emit('error', error.value)
    }
  }

  await poll()
}

const formatFileSize = (bytes) => {
  if (bytes < 1024) return bytes + ' B'
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
  return (bytes / (1024 * 1024)).toFixed(1) + ' MB'
}

const reset = () => {
  selectedFile.value = null
  error.value = null
  jobId.value = null
  queuePosition.value = null
  estimatedWait.value = null
  jobStatus.value = null
  processing.value = false  // Stop any ongoing long-poll
  if (fileInput.value) {
    fileInput.value.value = ''
  }
}

// Expose methods for parent components
defineExpose({ reset, processOCR })
</script>

<style scoped>
.ocr-upload-zone {
  margin-bottom: 1rem;
}

.upload-dropzone {
  border: 2px dashed var(--surface-border);
  border-radius: 10px;
  padding: 1rem 1.25rem;
  text-align: center;
  cursor: pointer;
  transition: all 0.3s ease;
  background: var(--surface-ground);
}

.upload-dropzone:hover {
  border-color: var(--primary-500);
  background: var(--surface-hover);
}

.upload-dropzone.dragging {
  border-color: var(--primary-500);
  background: var(--primary-50);
  transform: scale(1.02);
}

.upload-dropzone.processing {
  cursor: default;
  background: var(--surface-card);
}

.hidden-input {
  display: none;
}

/* Empty state - compact */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}

.main-text {
  font-size: 0.9rem;
  color: var(--text-color-secondary);
  margin: 0.25rem 0;
}

.sub-text {
  font-size: 0.8rem;
  color: var(--text-color-secondary);
  margin: 0;
}

/* File selected state - compact */
.file-selected-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
}

.file-name {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--text-color);
  margin: 0.25rem 0 0 0;
  word-break: break-all;
}

.file-size {
  font-size: 0.8rem;
  color: var(--text-color-secondary);
  margin: 0;
}

/* Action buttons (below dropzone) */
.action-buttons {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.75rem;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
}

/* Engine selector dropdown - PrimeVue styles in vendor/primevue-overrides.css */
.engine-selector {
  min-width: 180px;
}

/* Engine dropdown option with description */
.engine-option {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px 0;
}

.engine-label {
  font-weight: 500;
  font-size: 0.875rem;
  color: var(--text-color);
}

.engine-desc {
  font-size: 0.75rem;
  color: var(--text-color-secondary);
}

/* Processing state */
.processing-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

.processing-text {
  font-size: 1rem;
  color: var(--text-color-secondary);
  margin: 0.5rem 0 0 0;
  font-weight: 500;
}

.processing-subtext {
  font-size: 0.85rem;
  color: var(--text-color-secondary);
  margin: 0;
}
</style>
