<script setup>
/**
 * Multi-file drag-and-drop uploader for incoming invoices.
 * - One slim, visually-distinct drop row (always visible).
 * - Many files at once; each uploads via XMLHttpRequest (fetch can't report %).
 * - A compact progress panel shows an overall "X / Y hochgeladen" count plus a
 *   per-file green bar that fills left→right and flips to ✓ when that file is done.
 *
 * IMPORTANT reactivity note: every item is wrapped in reactive() *before* it goes
 * into the list. Mutating a plain object that was pushed into a ref([]) array does
 * NOT trigger re-renders (Vue stores the raw object; only its lazily-created proxy
 * is reactive), which is why the old version sat at 0% forever. The File handle is
 * kept off the reactive object (in the job) so Vue never proxies a native File.
 *
 * The server stores the blob + runs OCR in the BACKGROUND (fire-and-forget), so a
 * 200 here means "uploaded & queued" — the per-file processing state then lives on
 * the cards (Processing → Ready), polled by the parent. Emits `queued` per success.
 */
import { ref, reactive, computed } from 'vue'

const { t } = useTranslation()
const emit = defineEmits(['queued'])

const MAX_CONCURRENT = 3
const MAX_SIZE = 5 * 1024 * 1024 // 5 MB per file
const UPLOAD_TIMEOUT = 120000     // 2 min — beyond this the file errors instead of hanging forever
const ACCEPT = '.pdf,.xml,.png,.jpg,.jpeg,.tif,.tiff,application/pdf,text/xml,image/*'

const dragging = ref(false)
const fileInput = ref(null)
const items = ref([])  // reactive() items: { key, name, size, progress, status, error }
let seq = 0
let active = 0
let collapseTimer = null
const queue = []       // jobs: { item, file } — File kept OUT of the reactive item

const human = (n) => n > 1048576 ? (n / 1048576).toFixed(1) + ' MB' : (n / 1024).toFixed(0) + ' KB'

/* ---- aggregate progress ---- */
const total = computed(() => items.value.length)
const finished = computed(() => items.value.filter(i => i.status === 'done' || i.status === 'error').length)
const doneCount = computed(() => items.value.filter(i => i.status === 'done').length)
const errorCount = computed(() => items.value.filter(i => i.status === 'error').length)
const allFinished = computed(() => total.value > 0 && finished.value === total.value)
const overallPct = computed(() => {
  if (!total.value) return 0
  const sum = items.value.reduce((a, i) => a + (i.status === 'done' || i.status === 'finalizing' ? 100 : i.progress), 0)
  return Math.round(sum / total.value)
})

const pump = () => {
  while (active < MAX_CONCURRENT && queue.length) {
    const job = queue.shift()
    active++
    uploadOne(job).finally(() => { active--; pump(); maybeCollapse() })
  }
}

const uploadOne = ({ item, file }) => new Promise((resolve) => {
  const fd = new FormData()
  fd.append('file', file, item.name)
  const xhr = new XMLHttpRequest()
  xhr.open('POST', '/api/accounting/incoming-invoices/upload', true)
  xhr.withCredentials = true
  xhr.timeout = UPLOAD_TIMEOUT
  item.status = 'uploading'
  xhr.upload.onprogress = (e) => {
    if (e.lengthComputable) item.progress = Math.min(99, Math.round((e.loaded / e.total) * 100))
  }
  // fires when ALL bytes have been sent — guarantees the bar reaches 100% even
  // for tiny files that emit no granular progress events (the old "stuck at 0%").
  xhr.upload.onload = () => { item.progress = 100; if (item.status === 'uploading') item.status = 'finalizing' }
  xhr.onload = () => {
    try {
      const res = JSON.parse(xhr.responseText || '{}')
      if (xhr.status === 200 && res.id) {
        item.progress = 100
        item.status = 'done'
        emit('queued', res.id)
      } else {
        item.status = 'error'
        item.error = res.message || ('HTTP ' + xhr.status)
      }
    } catch {
      item.status = 'error'; item.error = 'Ungültige Server-Antwort'
    }
    resolve()
  }
  xhr.onerror = () => { item.status = 'error'; item.error = 'Netzwerkfehler'; resolve() }
  xhr.ontimeout = () => { item.status = 'error'; item.error = 'Zeitüberschreitung'; resolve() }
  xhr.send(fd)
})

const addFiles = (fileList) => {
  const files = Array.from(fileList || [])
  if (!files.length) return
  if (collapseTimer) { clearTimeout(collapseTimer); collapseTimer = null }
  for (const file of files) {
    const item = reactive({ key: ++seq, name: file.name, size: file.size, progress: 0, status: 'queued', error: '' })
    items.value.push(item)
    if (file.size > MAX_SIZE) {
      item.status = 'error'
      item.error = t.value?.file_too_large || 'Datei zu groß (max. 5 MB)'
      continue
    }
    queue.push({ item, file })
  }
  pump()
}

// auto-collapse the panel a few seconds after everything succeeds (errors stay visible)
const maybeCollapse = () => {
  if (allFinished.value && !errorCount.value) {
    collapseTimer = setTimeout(() => { items.value = [] }, 4000)
  }
}

const onDrop = (e) => { dragging.value = false; if (e.dataTransfer?.files?.length) addFiles(e.dataTransfer.files) }
const onPick = (e) => { if (e.target.files?.length) addFiles(e.target.files); e.target.value = '' }
const clearList = () => { if (collapseTimer) { clearTimeout(collapseTimer); collapseTimer = null } items.value = [] }
</script>

<template>
  <div class="inbox-upload">
    <!-- slim, colored, clearly-distinct drop row -->
    <div
      class="dropzone-row"
      :class="{ dragging }"
      @dragover.prevent="dragging = true"
      @dragleave.prevent="dragging = false"
      @drop.prevent="onDrop"
      @click="fileInput?.click()"
    >
      <input ref="fileInput" type="file" :accept="ACCEPT" multiple class="is-hidden" @change="onPick" />
      <span class="dz-badge"><i class="ti ti-plus"></i></span>
      <span class="dz-text">
        {{ t.add_documents || 'Belege hinzufügen' }}
        <span class="dz-muted">({{ t.drop_or_select || 'hier ablegen oder auswählen' }})</span>
      </span>
      <span class="dz-hint">{{ t.upload_formats || 'PDF, JPEG, PNG oder XML — max. 5 MB pro Datei' }}</span>
    </div>

    <!-- aggregate + per-file progress -->
    <div v-if="items.length" class="upload-panel">
      <div class="up-head">
        <div class="up-summary">
          <template v-if="allFinished && !errorCount">
            <i class="ti ti-circle-check up-ok"></i>
            <strong>{{ total }}</strong> {{ total === 1 ? (t.file || 'Datei') : (t.files || 'Dateien') }} {{ t.uploaded || 'hochgeladen' }}
          </template>
          <template v-else>
            <i class="ti ti-loader-2 spin up-spin"></i>
            <strong>{{ doneCount }}</strong> / {{ total }} {{ t.uploaded || 'hochgeladen' }}
            <span v-if="errorCount" class="up-errcount">· {{ errorCount }} {{ t.failed || 'Fehler' }}</span>
          </template>
        </div>
        <button class="up-clear" @click="clearList">{{ t.hide || 'Ausblenden' }}</button>
      </div>

      <!-- overall bar -->
      <div class="up-overall"><div class="up-overall-fill" :class="{ ok: allFinished && !errorCount }" :style="{ width: overallPct + '%' }"></div></div>

      <!-- per-file rows -->
      <div class="up-files">
        <div v-for="it in items" :key="it.key" class="uf-row" :class="it.status">
          <span class="uf-name" :title="it.name">{{ it.name }}</span>
          <span class="uf-bar">
            <span class="uf-fill" :class="it.status" :style="{ width: (it.status === 'done' || it.status === 'finalizing' ? 100 : it.progress) + '%' }"></span>
          </span>
          <span class="uf-state">
            <i v-if="it.status === 'done'" class="ti ti-check uf-done"></i>
            <i v-else-if="it.status === 'error'" class="ti ti-alert-triangle uf-err" :title="it.error"></i>
            <span v-else-if="it.status === 'queued'" class="uf-pct muted">{{ t.waiting || 'wartet' }}</span>
            <span v-else class="uf-pct">{{ it.status === 'finalizing' ? 100 : it.progress }}%</span>
          </span>
          <span class="uf-size">{{ human(it.size) }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
/* ---- distinct, colored drop row ---- */
.dropzone-row {
  display: flex; align-items: center; gap: 12px;
  border: 1.5px dashed #a5b4fc;
  border-radius: 12px;
  padding: 12px 16px;
  cursor: pointer;
  transition: all .15s ease;
  background: linear-gradient(135deg, rgba(99,102,241,.10), rgba(56,189,248,.07));
}
.dropzone-row:hover, .dropzone-row.dragging {
  border-color: #6366f1;
  background: linear-gradient(135deg, rgba(99,102,241,.20), rgba(56,189,248,.12));
  box-shadow: 0 6px 18px rgba(99,102,241,.18);
}
.dz-badge {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 9px;
  display: inline-flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg,#6366f1,#8b5cf6); color: #fff; font-size: 1.05rem;
  box-shadow: 0 3px 8px rgba(99,102,241,.35);
}
.dz-text { font-weight: 700; color: #3730a3; font-size: .92rem; }
.dz-muted { font-weight: 400; color: #6366f1; opacity: .85; }
.dz-hint { margin-left: auto; font-size: .76rem; color: #6b7280; white-space: nowrap; background: rgba(255,255,255,.6); border-radius: 6px; padding: 3px 8px; }
@media (max-width: 640px) { .dz-hint { display: none; } }

/* ---- progress panel ---- */
.upload-panel { margin-top: 14px; border: 1px solid var(--theme-border,#e6e8ec); border-radius: 12px; padding: 12px 14px; background: var(--theme-bg-primary,#fff); }
.up-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.up-summary { font-size: .86rem; color: var(--theme-text-primary,#334155); display: flex; align-items: center; gap: 7px; }
.up-summary strong { font-weight: 800; }
.up-ok { color: #16a34a; font-size: 1.05rem; }
.up-spin { color: #6366f1; }
.up-errcount { color: #b91c1c; font-weight: 600; }
.up-clear { border: none; background: transparent; color: var(--theme-text-secondary,#94a3b8); cursor: pointer; font-size: .8rem; }
.up-clear:hover { color: var(--theme-text-primary,#334155); }

.up-overall { height: 8px; background: var(--theme-bg-secondary,#eef2f7); border-radius: 5px; overflow: hidden; }
.up-overall-fill { height: 100%; background: linear-gradient(90deg,#6366f1,#22c55e); border-radius: 5px; transition: width .2s ease; }
.up-overall-fill.ok { background: #22c55e; }

.up-files { margin-top: 10px; display: flex; flex-direction: column; gap: 7px; max-height: 220px; overflow: auto; }
.uf-row { display: grid; grid-template-columns: minmax(0,1fr) 120px 46px 64px; align-items: center; gap: 10px; font-size: .82rem; }
.uf-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--theme-text-primary,#334155); }
.uf-row.error .uf-name { color: #b91c1c; }
.uf-bar { height: 6px; background: var(--theme-bg-secondary,#eef2f7); border-radius: 4px; overflow: hidden; }
.uf-fill { display: block; height: 100%; width: 0; background: #22c55e; border-radius: 4px; transition: width .2s ease; }
.uf-fill.uploading, .uf-fill.finalizing { background: #6366f1; }
.uf-fill.error { background: #ef4444; }
.uf-state { text-align: center; }
.uf-pct { font-size: .76rem; color: var(--theme-text-secondary,#64748b); }
.uf-pct.muted { color: var(--theme-text-secondary,#94a3b8); font-style: italic; }
.uf-done { color: #16a34a; }
.uf-err { color: #ef4444; }
.uf-size { text-align: right; color: var(--theme-text-secondary,#94a3b8); font-size: .74rem; }

.spin { animation: spin 1s linear infinite; display: inline-block; }
@keyframes spin { to { transform: rotate(360deg); } }
.is-hidden { display: none; }
@media (max-width: 560px) { .uf-row { grid-template-columns: minmax(0,1fr) 70px 40px; } .uf-size { display: none; } }
</style>
