<template>
  <span class="fc-tag-chip" @mouseenter="onEnter" @mouseleave="onLeave">
    <v-chip
      size="default" :closable="!unconfirmedAuto"
      :color="store.colorFor(tag.kind)" variant="tonal"
      class="fc-tag-chip__nav"
      role="link"
      :title="`Browse images tagged “${tag.name}”`"
      @click="$emit('navigate', tag)"
      @click:close="$emit('remove', tag.id)"
    >
      <v-icon start size="small">{{ iconFor(tag.kind) }}</v-icon>
      <span class="fc-tag-chip__name">{{ tag.name }}</span><v-icon
        v-if="tag.is_system" end size="x-small" class="fc-tag-chip__system"
        title="System tag — tagged items are excluded from training other concepts"
      >mdi-shield-outline</v-icon><span
        v-if="tag.fandom_id" class="fc-tag-chip__fandom"
        :title="tag.fandom_name ? `Fandom: ${tag.fandom_name}` : 'Has a fandom'"
      >→<template v-if="fandomLabel">&nbsp;{{ fandomLabel }}</template></span>
      <!-- Provisional auto-tag: an in-pill yes/no pair REPLACES the ✕ (the pair
           itself signals "auto" — no separate label, operator 2026-07-07). Yes
           confirms it (trains + shields from retraction); No removes it (records
           a negative). Both hand focus back to the tag input via TagPanel. -->
      <span v-if="unconfirmedAuto" class="fc-tag-chip__verdict">
        <button
          type="button" class="fc-tag-chip__yes"
          :title="`Yes — keep “${tag.name}” (trains the model, won't be retracted)`"
          :aria-label="`Confirm ${tag.name}`"
          @click.stop="$emit('confirm', tag)"
        ><v-icon size="15">mdi-check</v-icon></button>
        <button
          type="button" class="fc-tag-chip__no"
          :title="`No — remove “${tag.name}”`"
          :aria-label="`Reject ${tag.name}`"
          @click.stop="$emit('remove', tag.id)"
        ><v-icon size="15">mdi-close</v-icon></button>
      </span>
    </v-chip>
    <!-- Modal-safe kebab is baked into KebabMenu (this chip lives in the
         teleported image modal — #711). System tags hide it entirely: rename
         is refused server-side (the hygiene machinery keys on the row) and
         set-fandom never applies to their general kind. Remove (✕) stays —
         un-tagging an image is normal use. -->
    <KebabMenu
      v-if="!tag.is_system"
      class="fc-tag-chip__kebab" :label="`More actions for ${tag.name}`"
    >
      <v-list-item @click="$emit('rename', tag)">
        <v-list-item-title>Rename…</v-list-item-title>
      </v-list-item>
      <v-list-item v-if="tag.kind === 'character'" @click="$emit('set-fandom', tag)">
        <v-list-item-title>Set fandom…</v-list-item-title>
      </v-list-item>
    </KebabMenu>
  </span>
</template>

<script setup>
import { computed, inject } from 'vue'
import { useTagStore } from '../../stores/tags.js'
import { useApi } from '../../composables/useApi.js'
import KebabMenu from '../common/KebabMenu.vue'

const AUTO_SOURCES = ['head_auto', 'ccip_auto', 'ml_auto']

const props = defineProps({
  tag: { type: Object, required: true },
  // When set (the tagging panels), hovering the chip asks the backend which crop
  // region best explains this applied tag and lights it up on the image — the
  // same overlay the suggestion rail uses (#1206 Step 4). Omitted elsewhere →
  // the hover is inert (no injected target, or no image to ground against).
  imageId: { type: Number, default: null },
})
defineEmits(['remove', 'rename', 'set-fandom', 'navigate', 'confirm'])

const store = useTagStore()
const api = useApi()

// An auto-applied tag the operator hasn't confirmed yet — provisional (milestone
// 139): it doesn't train the model and the retraction sweep can drop it. Shows an
// in-pill yes/no pair in place of the ✕. `source`/`confirmed` come from the
// applied-tags payload (list_for_image / get_image_with_tags).
const unconfirmedAuto = computed(() =>
  AUTO_SOURCES.includes(props.tag.source) && !props.tag.confirmed
)

// #1206 Step 4: applied-tag grounding. `fcSuggestionHover` is provided by the
// image viewer / Explore host (a no-op elsewhere). Applied tags aren't scored
// live, so we fetch the winning region on demand and cache it per (image, tag).
const hover = inject('fcSuggestionHover', null)
const _groundingCache = new Map()
// Bumped on every enter/leave so a slow fetch that resolves after the pointer
// has moved on can't draw a stale overlay.
let hoverSeq = 0

async function onEnter () {
  if (!hover || props.imageId == null) return
  const seq = ++hoverSeq
  const key = `${props.imageId}:${props.tag.id}`
  let res = _groundingCache.get(key)
  if (res === undefined) {
    try {
      res = await api.get(
        `/api/images/${props.imageId}/tags/${props.tag.id}/grounding`
      )
      _groundingCache.set(key, res)
    } catch {
      return   // best-effort — leave the overlay untouched on error
    }
  }
  if (seq !== hoverSeq) return   // pointer already left / moved to another chip
  // No head → nothing to localize; don't draw an overlay at all. With a head,
  // null grounding still draws the whole-image frame ("the global vector won").
  if (res.has_head) hover.value = { g: res.grounding ?? null, tag: props.tag.name }
}
function onLeave () {
  hoverSeq++
  if (hover) hover.value = null
}

// Show a character's fandom inline (truncated). Falls back to a bare arrow when
// only fandom_id is known but the name wasn't resolved (older payloads).
const FANDOM_MAX = 15
const fandomLabel = computed(() => {
  const n = props.tag.fandom_name
  if (!n) return ''
  return n.length > FANDOM_MAX ? `${n.slice(0, FANDOM_MAX)}…` : n
})

const KIND_ICONS = {
  general: 'mdi-tag', character: 'mdi-account-circle',
  fandom: 'mdi-book-open-page-variant', series: 'mdi-bookshelf',
  meta: 'mdi-cog-outline', rating: 'mdi-shield-check-outline',
}
function iconFor (k) { return KIND_ICONS[k] || 'mdi-tag' }
</script>

<style scoped>
/* A chip must never exceed the rail — a long character+fandom chip used to
   overflow the right edge and clip its trailing control (operator-flagged
   2026-07-06). The name is the elastic part: it ellipsis-truncates so the ✕ (or
   the auto-tag yes/no pair) and fandom stay reachable. Full name is on the
   chip's hover title. */
.fc-tag-chip { display: inline-flex; align-items: center; gap: 1px; max-width: 100%; min-width: 0; }
.fc-tag-chip__nav { max-width: 100%; min-width: 0; }
/* Tonal chips (esp. character = info) wash out against the dark rail — the fill
   is intentionally faint. Give every tag chip a thin border in its OWN kind
   colour (currentColor = the tonal chip's themed foreground) so the edge reads
   clearly without touching the fill (operator-asked 2026-07-08). Theme-aware:
   currentColor tracks the kind colour in either light or dark. */
.fc-tag-chip__nav { border: thin solid color-mix(in srgb, currentColor 55%, transparent); }
.fc-tag-chip__nav :deep(.v-chip__content) { min-width: 0; overflow: hidden; }
/* The bigger size=default chip widens Vuetify's negative start-margin on the
   leading kind-icon, pulling it LEFT of the content box above — where that
   overflow:hidden then clips the icon's left edge as a vertical slice
   (operator-flagged 2026-07-08). Zero the pull so the icon sits inside the clip
   box; the chip's own 12px padding keeps a comfortable left inset. */
.fc-tag-chip__nav :deep(.v-icon--start) { margin-inline-start: 0; }
.fc-tag-chip__name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-width: 0; flex: 0 1 auto;
}
/* The chip body navigates to the filtered gallery (#5); signal it's clickable.
   The close ✕ (remove) and the sibling kebab stay as the explicit controls. */
.fc-tag-chip__nav { cursor: pointer; }
.fc-tag-chip__nav:focus-visible {
  outline: 2px solid rgb(var(--v-theme-accent));
  outline-offset: 1px;
}
.fc-tag-chip__system { opacity: 0.6; margin-left: 2px; }
.fc-tag-chip__kebab { opacity: 0.7; }
.fc-tag-chip:hover .fc-tag-chip__kebab { opacity: 1; }
.fc-tag-chip__fandom { opacity: 0.7; font-size: 0.85em; }
/* Provisional auto-tag: a green ✓ / red ✗ pair in place of the ✕. The yes/no is
   obvious enough on its own, so there's no "auto" label (operator-asked
   2026-07-07). flex:0 0 auto keeps it visible while the name ellipsis-truncates. */
.fc-tag-chip__verdict {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 3px;
  margin-left: 5px;
}
/* Solid-filled (white glyph on a full success/error circle) so accept/reject
   stay well-defined even on a muted tonal chip — e.g. character (info) — instead
   of a faint icon that only lit up on hover. Mirrors the Suggestions rail's
   verdict buttons so the affordance reads identically (operator-asked 2026-07-08). */
.fc-tag-chip__yes, .fc-tag-chip__no {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; border: none; border-radius: 50%;
  color: #fff; cursor: pointer; opacity: 0.92;
  transition: transform 0.1s, opacity 0.1s;
}
.fc-tag-chip__yes { background: rgb(var(--v-theme-success)); }
.fc-tag-chip__no { background: rgb(var(--v-theme-error)); }
.fc-tag-chip__yes:hover, .fc-tag-chip__no:hover { opacity: 1; transform: scale(1.1); }
.fc-tag-chip__yes:focus-visible, .fc-tag-chip__no:focus-visible {
  outline: 2px solid rgb(var(--v-theme-accent)); outline-offset: 2px;
}
</style>
