<template>
  <v-card>
    <CardHeading title="Recent import tasks">
      <v-spacer />
      <v-select
        v-model="statusFilter" :items="statusOptions" density="compact"
        hide-details style="max-width: 180px;" @update:model-value="onFilterChange"
      />
      <v-btn variant="text" rounded="pill" size="small" @click="onRefresh">
        <v-icon start>mdi-refresh</v-icon>
        Refresh
      </v-btn>
      <v-btn
        variant="text" rounded="pill" size="small" color="warning"
        :disabled="!hasFailed" @click="onRetryFailed"
      >
        Retry failed
      </v-btn>
      <v-btn
        variant="text" rounded="pill" size="small" color="warning"
        :disabled="!hasStuck" @click="onClearStuckOpen"
      >
        Clear stuck…
      </v-btn>
      <v-btn
        variant="text" rounded="pill" size="small" color="error"
        @click="onClearOpen"
      >
        Clear completed…
      </v-btn>
    </CardHeading>
    <v-data-table-virtual
      :headers="headers" :items="store.tasks" :loading="store.tasksLoading"
      height="480" density="compact" no-data-text="No tasks yet — trigger a scan above."
    >
      <template #item.status="{ item }">
        <v-chip :color="statusColor(item.status)" size="small" variant="tonal">
          {{ item.status }}
        </v-chip>
      </template>
      <template #item.source_path="{ item }">
        <span :title="item.source_path">{{ shorten(item.source_path) }}</span>
      </template>
      <template #item.size_bytes="{ item }">{{ formatBytes(item.size_bytes) }}</template>
      <template #item.created_at="{ item }">{{ formatDate(item.created_at) }}</template>
      <template #item.error="{ item }">
        <button
          v-if="item.error" type="button" class="fc-err-link text-caption"
          @click="openError(`Task ${item.id} failed`, item.error)"
          title="Click for full error"
        >{{ shorten(item.error, 60) }}</button>
      </template>
      <template #item.actions="{ item }">
        <v-btn
          v-if="item.status === 'failed'"
          icon size="x-small" variant="text"
          :loading="refetching === item.id"
          @click="onRefetch(item)"
        >
          <v-icon size="small">mdi-cloud-refresh</v-icon>
          <v-tooltip activator="parent" location="top">
            Re-fetch original (re-download from source)
          </v-tooltip>
        </v-btn>
      </template>
    </v-data-table-virtual>
    <div v-if="store.hasMore" class="d-flex justify-center py-3">
      <v-btn variant="text" size="small" @click="onLoadMore">Load more</v-btn>
    </div>

    <v-dialog v-model="clearDialog" max-width="400">
      <v-card>
        <v-card-title>Clear completed tasks</v-card-title>
        <v-card-text>
          <v-select
            v-model="clearAgeDays" label="Older than"
            :items="[
              { title: 'All finished', value: 0 },
              { title: '1 day',  value: 1 },
              { title: '7 days', value: 7 },
              { title: '30 days', value: 30 }
            ]"
          />
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn @click="clearDialog = false">Cancel</v-btn>
          <v-btn color="error" rounded="pill" @click="onClearConfirm">Clear</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <v-dialog v-model="clearStuckDialog" max-width="480">
      <v-card>
        <v-card-title>Clear stuck tasks</v-card-title>
        <v-card-text>
          <v-alert type="warning" variant="tonal" density="compact" class="mb-3">
            Force every <strong>pending / queued / processing</strong> task to
            <strong>failed</strong> and finalize any active batch that
            has no remaining work. Use this when the automatic recovery
            sweep keeps re-queueing the same row (e.g., corrupt file in
            an autoretry loop, or worker model missing).
          </v-alert>
          <p class="text-body-2">
            Tasks remain in the database with status=<code>failed</code>;
            click <em>Retry failed</em> once the underlying cause is
            resolved to re-queue them.
          </p>
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn @click="clearStuckDialog = false">Cancel</v-btn>
          <v-btn color="warning" rounded="pill" @click="onClearStuckConfirm">Clear stuck</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <ErrorDetailModal
      v-model="showErrorModal"
      :title="errorModalTitle"
      :message="errorModalMessage"
    />
  </v-card>
</template>

<script setup>
import { toast } from '../../utils/toast.js'
import { computed, ref } from 'vue'
import { useImportStore } from '../../stores/import.js'
import ErrorDetailModal from '../common/ErrorDetailModal.vue'
import CardHeading from '../common/CardHeading.vue'

const store = useImportStore()
const statusFilter = ref(null)
const clearDialog = ref(false)
// Click-to-open modal for full error text (operator-flagged 2026-05-26
// — the prior :title="..." tooltip cramped multi-line SQLAlchemy
// tracebacks into an unusable popup with no copy-paste affordance).
const showErrorModal = ref(false)
const errorModalTitle = ref('')
const errorModalMessage = ref('')

function openError(title, message) {
  errorModalTitle.value = title
  errorModalMessage.value = message || ''
  showErrorModal.value = true
}
const clearAgeDays = ref(7)
const clearStuckDialog = ref(false)

const statusOptions = [
  { title: 'All', value: null },
  { title: 'Pending', value: 'pending' },
  { title: 'Queued', value: 'queued' },
  { title: 'Processing', value: 'processing' },
  { title: 'Complete', value: 'complete' },
  { title: 'Skipped', value: 'skipped' },
  { title: 'Failed', value: 'failed' }
]

const headers = [
  { title: 'Status', key: 'status', sortable: false, width: 120 },
  { title: 'Source', key: 'source_path', sortable: false },
  { title: 'Size', key: 'size_bytes', sortable: false, width: 90 },
  { title: 'Created', key: 'created_at', sortable: false, width: 150 },
  { title: 'Note', key: 'error', sortable: false },
  { title: '', key: 'actions', sortable: false, width: 56 }
]

const refetching = ref(null)
const _REFETCH_MSG = {
  refetch_queued: { text: 'Re-fetch queued — re-downloading from source', type: 'success' },
  no_source: { text: 'No re-fetchable source (filesystem import — replace the file manually)', type: 'info' },
  already_refetched: { text: 'Already re-fetched once', type: 'info' },
}
async function onRefetch(item) {
  refetching.value = item.id
  try {
    const res = await store.refetchTask(item.id)
    const msg = _REFETCH_MSG[res.status] || { text: `Re-fetch: ${res.status}`, type: 'info' }
    toast(msg)
  } catch (e) {
    toast({ text: `Re-fetch failed: ${e.message}`, type: 'error' })
  } finally {
    refetching.value = null
  }
}

const hasFailed = computed(() => store.tasks.some(t => t.status === 'failed'))
const hasStuck = computed(() => store.tasks.some(
  t => t.status === 'pending' || t.status === 'queued' || t.status === 'processing'
))

function statusColor(s) {
  return {
    complete: 'success',
    skipped: 'warning',
    failed: 'error',
    processing: 'accent',
    queued: 'info',
    pending: 'info'
  }[s] || 'default'
}
function shorten(s, max = 90) {
  if (!s) return ''
  if (s.length <= max) return s
  const head = Math.floor((max - 3) * 0.6)
  const tail = max - 3 - head
  return s.slice(0, head) + '...' + s.slice(-tail)
}
function formatBytes(b) {
  if (!b) return ''
  const units = ['B', 'KiB', 'MiB', 'GiB']
  let i = 0; let v = b
  while (v >= 1024 && i < units.length - 1) { v /= 1024; i++ }
  return `${v.toFixed(i === 0 ? 0 : 1)} ${units[i]}`
}
function formatDate(s) {
  try { return new Date(s).toLocaleString() } catch { return s }
}

async function onRefresh() { await store.loadTasks(true) }
function onFilterChange() { store.setStatusFilter(statusFilter.value); store.loadTasks(true) }
async function onLoadMore() { await store.loadTasks(false) }
async function onRetryFailed() { await store.retryFailed() }
function onClearOpen() { clearDialog.value = true }
async function onClearConfirm() {
  await store.clearCompleted(clearAgeDays.value)
  clearDialog.value = false
}
function onClearStuckOpen() { clearStuckDialog.value = true }
async function onClearStuckConfirm() {
  await store.clearStuck()
  clearStuckDialog.value = false
}
</script>

<style scoped>
.fc-err-link {
  /* Truncated error preview as a clickable button — opens
     ErrorDetailModal with the full text. Inherits the row's font
     sizing so it doesn't visually drift from the prior tooltip-bearing
     span. */
  color: rgb(var(--v-theme-error, 220 80 80));
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  text-align: left;
  text-decoration: underline dotted;
  cursor: pointer;
}
.fc-err-link:hover { text-decoration: underline; }
</style>
