<template>
  <v-card>
    <v-card-title class="text-body-1">Fandom for “{{ tag.name }}”</v-card-title>
    <v-card-text>
      <template v-if="!collision">
        <!-- Keyboard flow matches FandomPicker (operator-specified 2026-06-07):
             focus lands here via the parent dialog's @after-enter → focusSearch
             (autofocus is unreliable inside a v-dialog focus-trap); Tab moves to
             the new-fandom field; a SECOND Enter (menu closed) accepts the choice
             by Saving instead of re-opening the dropdown. -->
        <v-autocomplete
          ref="fandomRef"
          v-model="selectedId"
          v-model:menu="menuOpen"
          :items="store.fandomCache"
          :item-title="(f) => f.name" :item-value="(f) => f.id"
          label="Fandom" clearable density="compact"
          :hint="selectedId == null
            ? 'No fandom — the character will be unassigned.' : ''"
          persistent-hint
          @keydown.enter.capture="onSearchEnter"
          @keydown.tab="onSearchTab"
        />
        <v-divider class="my-3" />
        <p class="text-caption mb-2">Or create a new fandom:</p>
        <div class="d-flex" style="gap: 8px;">
          <v-text-field
            ref="newNameRef"
            v-model="newName" placeholder="New fandom name"
            density="compact" hide-details
            @keydown.enter.prevent="onCreate"
          />
          <v-btn
            :disabled="!newName.trim() || busy" rounded="pill"
            @click="onCreate"
          >Create</v-btn>
        </div>
        <v-alert
          v-if="error" type="error" variant="tonal" density="compact"
          class="mt-3"
        >{{ error }}</v-alert>
      </template>

      <template v-else>
        <v-alert type="warning" variant="tonal" density="compact" class="mb-3">
          A character named “{{ tag.name }}” already exists in that fandom.
        </v-alert>
        <p class="text-body-2">
          Merge this tag into “{{ collision.target.name }}”?
          {{ collision.source_image_count }} image
          association{{ collision.source_image_count === 1 ? '' : 's' }}
          will move over and this tag will be deleted{{
            collision.will_alias ? ' (its name kept as a tagger alias)' : '' }}.
        </p>
      </template>
    </v-card-text>
    <v-card-actions>
      <v-spacer />
      <template v-if="!collision">
        <v-btn variant="text" :disabled="busy" @click="$emit('cancel')">
          Cancel
        </v-btn>
        <v-btn
          color="primary" rounded="pill" :loading="busy"
          :disabled="selectedId === (tag.fandom_id ?? null)"
          @click="onSave"
        >Save</v-btn>
      </template>
      <template v-else>
        <v-btn variant="text" :disabled="busy" @click="collision = null">
          Back
        </v-btn>
        <v-btn
          color="warning" variant="flat" rounded="pill" :loading="busy"
          @click="onConfirmMerge"
        >Merge</v-btn>
      </template>
    </v-card-actions>
  </v-card>
</template>

<script setup>
import { nextTick, onMounted, ref } from 'vue'
import { useTagStore } from '../../stores/tags.js'
import { useAcceptOnEnter } from '../../composables/useAcceptOnEnter.js'

const props = defineProps({ tag: { type: Object, required: true } })
const emit = defineEmits(['updated', 'cancel'])

const store = useTagStore()

const selectedId = ref(props.tag.fandom_id ?? null)
const newName = ref('')
const busy = ref(false)
const error = ref(null)
const collision = ref(null)
const fandomRef = ref(null)
const newNameRef = ref(null)

// Enter on the closed dropdown Saves the changed fandom instead of re-opening it.
const { menuOpen, onEnter: onSearchEnter } = useAcceptOnEnter(() => {
  if (busy.value) return
  if (selectedId.value !== (props.tag.fandom_id ?? null)) onSave()
})

// Always refresh on open so the list reflects fandoms created elsewhere (#712).
onMounted(() => store.loadFandoms())

// Exposed so the parent dialog can focus the search field on @after-enter —
// the reliable point to grab focus (the dialog transition + focus-trap are done).
function focusSearch() {
  nextTick(() => {
    fandomRef.value?.focus?.()
    // Keep the menu closed after a programmatic focus so the very next Enter
    // accepts the value (Saves) instead of being swallowed as a menu interaction.
    menuOpen.value = false
  })
}
defineExpose({ focusSearch })

async function onCreate() {
  const name = newName.value.trim()
  if (!name) return
  busy.value = true
  error.value = null
  try {
    const f = await store.createFandom(name)
    selectedId.value = f.id
    newName.value = ''
    // The created fandom is now the selection; hand focus back to the dropdown
    // so a single Enter saves it (matches FandomPicker's flow).
    focusSearch()
  } catch (e) {
    error.value = e.message || String(e)
  } finally {
    busy.value = false
  }
}

// Tab from the search field goes to the "new fandom" text field (operator-
// specified). Shift+Tab keeps normal reverse traversal.
function onSearchTab(e) {
  if (e.shiftKey) return
  e.preventDefault()
  menuOpen.value = false
  nextTick(() => newNameRef.value?.focus?.())
}

async function save(merge) {
  busy.value = true
  error.value = null
  try {
    const body = await store.setFandom(props.tag.id, selectedId.value, { merge })
    emit('updated', body)
  } catch (e) {
    // 409 on first attempt → surface the merge confirmation; cross-fandom
    // collisions can't go through the regular /merge endpoint, so the
    // resolution is a second setFandom with merge: true.
    if (!merge && e.status === 409 && e.body && e.body.target) {
      collision.value = e.body
    } else {
      error.value = e.message || String(e)
      collision.value = null
    }
  } finally {
    busy.value = false
  }
}

function onSave() { save(false) }
function onConfirmMerge() { save(true) }
</script>
