<template>
  <MaintenanceTile
    icon="mdi-playlist-check"
    :title="`Allowlisted tags (${store.rows.length})`"
    blurb="Tags auto-applied to images that score above their threshold. Tune the
           threshold and see how many images it would cover."
  >
    <v-data-table-virtual
      :headers="headers" :items="store.rows" :loading="store.loading"
      height="360" density="compact"
      no-data-text="No tags on the allowlist yet — accept a suggestion to add one."
    >
      <template #item.applied_count="{ item }">
        <span class="fc-num">{{ item.applied_count ?? '—' }}</span>
      </template>

      <template #item.min_confidence="{ item }">
        <div class="fc-thr">
          <v-text-field
            :model-value="item.min_confidence" type="number"
            density="compact" hide-details style="max-width: 100px;"
            :min="floor" max="1" step="0.05"
            :aria-label="`Auto-apply threshold for ${item.tag_name}`"
            @update:model-value="(v) => onThreshold(item, v)"
          />
          <span
            v-if="proj[item.tag_id]"
            class="fc-thr__proj"
            :class="{ 'fc-thr__proj--loading': proj[item.tag_id].loading }"
            :title="`At ${proj[item.tag_id].threshold}, a sweep would cover this many images`"
          >≈ {{ proj[item.tag_id].count }} at {{ proj[item.tag_id].threshold }}</span>
        </div>
      </template>

      <template #item.coverage_count="{ item }">
        <span class="fc-num" :title="`Images a sweep covers at ${item.min_confidence}`">
          {{ item.coverage_count ?? '—' }}
        </span>
      </template>

      <template #item.actions="{ item }">
        <v-btn
          icon="mdi-delete" size="x-small" variant="text" color="error"
          :aria-label="`Remove ${item.tag_name} from the allowlist`"
          @click="store.remove(item.tag_id)"
        />
      </template>
    </v-data-table-virtual>
    <p class="fc-muted text-caption mt-2">
      <strong>Applied</strong> = images currently carrying the tag.
      <strong>Covers</strong> = images a sweep would auto-apply it to at the
      current threshold. Lower the threshold to cover more (less certain) images.
    </p>
  </MaintenanceTile>
</template>

<script setup>
import { computed, onMounted, reactive } from 'vue'
import { useAllowlistStore } from '../../stores/allowlist.js'
import { useMLStore } from '../../stores/ml.js'
import MaintenanceTile from '../common/MaintenanceTile.vue'

const store = useAllowlistStore()
const ml = useMLStore()
// min_confidence can't be set below the tagger store floor — predictions
// below it aren't stored, so a lower threshold would behave identically to
// the floor. The backend clamps too (#764).
const floor = computed(() => ml.settings?.tagger_store_floor ?? 0.70)
const headers = [
  { title: 'Tag', key: 'tag_name', sortable: true },
  { title: 'Kind', key: 'tag_kind', sortable: true, width: 100 },
  { title: 'Applied', key: 'applied_count', sortable: true, width: 90 },
  { title: 'Min confidence', key: 'min_confidence', sortable: false, width: 220 },
  { title: 'Covers', key: 'coverage_count', sortable: true, width: 90 },
  { title: '', key: 'actions', sortable: false, width: 56 }
]

// Per-row live projection while the operator drags a threshold:
//   proj[tagId] = { threshold, count, loading }
const proj = reactive({})

onMounted(() => {
  store.load()
  if (!ml.settings) ml.loadSettings()
})

const debounces = {}
function onThreshold(item, value) {
  const tagId = item.tag_id
  const v = Math.max(parseFloat(value), floor.value)
  if (!(v > 0 && v <= 1)) return
  const shown = Number(v.toFixed(2))
  // Optimistic live projection box (loading until the count returns).
  proj[tagId] = { threshold: shown, count: proj[tagId]?.count ?? '…', loading: true }
  if (debounces[tagId]) clearTimeout(debounces[tagId])
  debounces[tagId] = setTimeout(async () => {
    try {
      const { count } = await store.coverage(tagId, v)
      proj[tagId] = { threshold: shown, count, loading: false }
    } catch {
      delete proj[tagId]  // drop the projection rather than show a wrong number
    }
    // Commit the new threshold (also refreshes the row's stored coverage_count).
    store.updateThreshold(tagId, v)
  }, 500)
}
</script>

<style scoped>
.fc-num { font-variant-numeric: tabular-nums; }
.fc-thr { display: flex; align-items: center; gap: 10px; }
.fc-thr__proj {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: rgb(var(--v-theme-accent));
  white-space: nowrap;
}
.fc-thr__proj--loading { color: rgb(var(--v-theme-on-surface-variant)); }
.fc-muted { color: rgb(var(--v-theme-on-surface-variant)); }
</style>
