<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import SearchSelect from '@/components/SearchSelect.vue'
import GroupedSearchSelect from '@/components/GroupedSearchSelect.vue'
import DkuSpinner from '@/components/DkuSpinner.vue'
import { glossaryApi, dssResourcesApi } from '@/api'
import { useAppStore } from '@/stores/app'
import { useSemanticModelsStore } from '@/stores/semantic-models'
import { toast } from 'vue-sonner'
import type { GroupedOption } from '@/components/GroupedSearchSelect.vue'
import type {
  SemanticModelSpec,
  GlossaryTerm,
  GlossaryBinding,
  GlossaryBindingCreate,
  BindingSuggestion,
  BindingTargetType,
  DSSLLM,
} from '@/types'

// Props
const props = defineProps<{
  modelId: string
  versionId: string
  spec: SemanticModelSpec
}>()

// Store
const appStore = useAppStore()
const semanticModelsStore = useSemanticModelsStore()
const { selectedLlmId } = storeToRefs(appStore)
const { draftGlossaryTerms, draftGlossaryBindings, draftVocabularyTermIds } =
  storeToRefs(semanticModelsStore)
const { t } = useI18n()

// State
const isLoading = ref(false)
const isSuggesting = ref(false)
const showAddBindingDialog = ref(false)
const showEditBindingDialog = ref(false)
const showSuggestionsDialog = ref(false)
const showDeleteDialog = ref(false)
const showBulkDeleteDialog = ref(false)
const bindingToDelete = ref<{ binding: GlossaryBinding; termName: string } | null>(null)
const bindingToEdit = ref<GlossaryBinding | null>(null)

// Multi-select state
const selectedBindingKeys = ref<Set<string>>(new Set())

// Add binding form
const newBinding = ref<GlossaryBindingCreate>({
  termId: '',
  targetEntityClass: '',
  targetType: 'ENTITY_CLASS',
  targetName: undefined,
})

// Edit binding form
const editBinding = ref<{
  targetEntityClass: string
  targetType: BindingTargetType
  targetName: string | undefined
}>({
  targetEntityClass: '',
  targetType: 'ENTITY_CLASS',
  targetName: undefined,
})

// LLM suggestions
const suggestions = ref<BindingSuggestion[]>([])
const selectedSuggestions = ref<Set<number>>(new Set())
const hasRequestedSuggestions = ref(false)

// LLMs
const availableLlms = ref<DSSLLM[]>([])
const isLoadingLlms = ref(false)

// Searchable dropdowns
const vocabularySearch = ref('')
const showVocabularyDropdown = ref(false)
const termSearch = ref('')
const showTermDropdown = ref(false)

// Blur handlers (need functions because templates don't have access to setTimeout/window)
function handleVocabularyBlur() {
  setTimeout(() => {
    showVocabularyDropdown.value = false
  }, 200)
}

function handleTermBlur() {
  setTimeout(() => {
    showTermDropdown.value = false
  }, 200)
}

// Computed
const terms = computed(() => draftGlossaryTerms.value)
const termsById = computed(() => {
  const map: Record<string, GlossaryTerm> = {}
  for (const term of terms.value) {
    map[term.id] = term
  }
  return map
})

const bindings = computed(() => draftGlossaryBindings.value)
const vocabularyTermIds = computed(() => draftVocabularyTermIds.value)

// Multi-select computed
const selectedCount = computed(() => selectedBindingKeys.value.size)
const hasSelection = computed(() => selectedCount.value > 0)
const allSelected = computed(() =>
  bindings.value.length > 0 && selectedBindingKeys.value.size === bindings.value.length
)
const someSelected = computed(() =>
  selectedBindingKeys.value.size > 0 && selectedBindingKeys.value.size < bindings.value.length
)

function isBindingSelected(binding: GlossaryBinding): boolean {
  return selectedBindingKeys.value.has(bindingKey(binding))
}

function toggleBindingSelection(binding: GlossaryBinding) {
  const key = bindingKey(binding)
  if (selectedBindingKeys.value.has(key)) {
    selectedBindingKeys.value.delete(key)
  } else {
    selectedBindingKeys.value.add(key)
  }
  // Trigger reactivity
  selectedBindingKeys.value = new Set(selectedBindingKeys.value)
}

function toggleSelectAll() {
  if (allSelected.value) {
    selectedBindingKeys.value = new Set()
  } else {
    selectedBindingKeys.value = new Set(bindings.value.map(bindingKey))
  }
}

function clearSelection() {
  selectedBindingKeys.value = new Set()
}

function handleBulkDelete() {
  if (selectedCount.value === 0) return
  showBulkDeleteDialog.value = true
}

function confirmBulkDelete() {
  draftGlossaryBindings.value = draftGlossaryBindings.value.filter(
    (b) => !selectedBindingKeys.value.has(bindingKey(b))
  )
  toast.success(t('bindings.deleted', selectedCount.value))
  clearSelection()
  showBulkDeleteDialog.value = false
}

function isTermBound(termId: string): boolean {
  return bindings.value.some((b) => b.termId === termId)
}

function isTermInVocabulary(termId: string): boolean {
  return vocabularyTermIds.value.includes(termId)
}

// Terms that can be bound (not already in vocabulary for this version)
const bindableTerms = computed(() =>
  terms.value.filter((t) => !isTermInVocabulary(t.id))
)

// Terms that can be added to vocabulary (not already bound)
const vocabularyEligibleTerms = computed(() =>
  terms.value.filter((t) => !isTermBound(t.id) && !isTermInVocabulary(t.id))
)

// Filtered vocabulary terms for search
const filteredVocabularyTerms = computed(() => {
  const search = vocabularySearch.value.toLowerCase().trim()
  if (!search) return vocabularyEligibleTerms.value
  return vocabularyEligibleTerms.value.filter(
    (t) =>
      t.term.toLowerCase().includes(search) ||
      t.description?.toLowerCase().includes(search) ||
      t.synonyms?.some((s) => s.toLowerCase().includes(search))
  )
})

// Filtered bindable terms for search in add binding dialog
const filteredBindableTerms = computed(() => {
  const search = termSearch.value.toLowerCase().trim()
  if (!search) return bindableTerms.value
  return bindableTerms.value.filter(
    (t) =>
      t.term.toLowerCase().includes(search) ||
      t.description?.toLowerCase().includes(search) ||
      t.synonyms?.some((s) => s.toLowerCase().includes(search))
  )
})

const llmOptions = computed((): GroupedOption[] =>
  availableLlms.value.map((llm) => ({
    value: llm.id,
    label: llm.label || llm.id,
    type: llm.type || 'Unknown',
    connection: llm.connection,
  }))
)

const hasLlms = computed(() => llmOptions.value.length > 0)

// Get entity name from ID
function getEntityName(entityId: string): string {
  const entity = props.spec.entities.find((e) => e.name === entityId)
  return entity?.name || entityId || '?'
}

// Get target name from binding
function getTargetName(binding: GlossaryBinding): string {
  if (binding.targetType === 'ENTITY_CLASS') {
    return '-'
  }
  const entity = props.spec.entities.find((e) => e.name === binding.targetEntityClass)
  if (!entity) return '?'

  if (binding.targetType === 'ATTRIBUTE') {
    const attr = entity.attributes.find((a) => a.name === binding.targetName)
    return attr?.name || '?'
  } else if (binding.targetType === 'METRIC') {
    const metric = entity.metrics.find((m) => m.name === binding.targetName)
    return metric?.name || '?'
  } else if (binding.targetType === 'FILTER') {
    const filter = entity.filters.find((f) => f.name === binding.targetName)
    return filter?.name || '?'
  }
  return '?'
}

// Get term name from ID
function getTermName(termId: string): string {
  return termsById.value[termId]?.term || termId
}

function bindingKey(binding: GlossaryBinding): string {
  return `${binding.termId}::${binding.targetEntityClass}::${binding.targetType}::${binding.targetName || ''}`
}

function formatTargetType(targetType: BindingTargetType): string {
  switch (targetType) {
    case 'ENTITY_CLASS':
      return 'Entity'
    case 'ATTRIBUTE':
      return 'Attribute'
    case 'METRIC':
      return 'Metric'
    case 'FILTER':
      return 'Filter'
    default:
      return targetType
  }
}

// Available targets for the selected entity/type
const availableTargets = computed(() => {
  const entity = props.spec.entities.find((e) => e.name === newBinding.value.targetEntityClass)
  if (!entity) return []

  switch (newBinding.value.targetType) {
    case 'ATTRIBUTE':
      return entity.attributes.map((a) => ({ id: a.name, name: a.name }))
    case 'METRIC':
      return entity.metrics.map((m) => ({ id: m.name, name: m.name }))
    case 'FILTER':
      return entity.filters.map((f) => ({ id: f.name, name: f.name }))
    default:
      return []
  }
})

// Reset target when entity or type changes
watch(
  () => [newBinding.value.targetEntityClass, newBinding.value.targetType],
  () => {
    if (newBinding.value.targetType === 'ENTITY_CLASS') {
      newBinding.value.targetName = undefined
    } else {
      newBinding.value.targetName = ''
    }
  }
)

// Available targets for edit form
const editAvailableTargets = computed(() => {
  const entity = props.spec.entities.find((e) => e.name === editBinding.value.targetEntityClass)
  if (!entity) return []

  switch (editBinding.value.targetType) {
    case 'ATTRIBUTE':
      return entity.attributes.map((a) => ({ id: a.name, name: a.name }))
    case 'METRIC':
      return entity.metrics.map((m) => ({ id: m.name, name: m.name }))
    case 'FILTER':
      return entity.filters.map((f) => ({ id: f.name, name: f.name }))
    default:
      return []
  }
})

// Options for SearchSelect components
const entityOptions = computed(() =>
  props.spec.entities.map((e) => ({ value: e.name, label: e.name }))
)

const targetTypeOptions = [
  { value: 'ENTITY_CLASS', label: 'Entity (whole entity)' },
  { value: 'ATTRIBUTE', label: 'Attribute' },
  { value: 'METRIC', label: 'Metric' },
  { value: 'FILTER', label: 'Filter' },
]

const availableTargetOptions = computed(() =>
  availableTargets.value.map((t) => ({ value: t.id, label: t.name }))
)

const editAvailableTargetOptions = computed(() =>
  editAvailableTargets.value.map((t) => ({ value: t.id, label: t.name }))
)

// Reset target when entity or type changes in edit form
watch(
  () => [editBinding.value.targetEntityClass, editBinding.value.targetType],
  () => {
    if (editBinding.value.targetType === 'ENTITY_CLASS') {
      editBinding.value.targetName = undefined
    } else {
      editBinding.value.targetName = ''
    }
  }
)

// No backend loading; glossary data lives in the draft store.

// =============================================================================
// Binding Actions
// =============================================================================

async function createBinding() {
  if (!newBinding.value.termId) {
    toast.error('Select a term')
    return
  }
  if (!newBinding.value.targetEntityClass) {
    toast.error('Select an entity')
    return
  }
  if (newBinding.value.targetType !== 'ENTITY_CLASS' && !newBinding.value.targetName) {
    toast.error('Target element is required')
    return
  }

  try {
    const payload: GlossaryBindingCreate = {
      termId: newBinding.value.termId,
      targetEntityClass: newBinding.value.targetEntityClass,
      targetType: newBinding.value.targetType,
      targetName:
        newBinding.value.targetType === 'ENTITY_CLASS'
          ? undefined
          : newBinding.value.targetName || undefined,
    }
    const binding: GlossaryBinding = { ...payload }
    const key = bindingKey(binding)
    const exists = draftGlossaryBindings.value.some((b) => bindingKey(b) === key)
    if (exists) {
      toast.error('This binding already exists')
      return
    }
    draftGlossaryBindings.value.push(binding)
    toast.success('Binding created')
    showAddBindingDialog.value = false
    resetNewBinding()
  } catch (e) {
    toast.error('Failed to create binding')
  }
}

function openDeleteDialog(binding: GlossaryBinding) {
  bindingToDelete.value = {
    binding,
    termName: getTermName(binding.termId),
  }
  showDeleteDialog.value = true
}

function openEditDialog(binding: GlossaryBinding) {
  bindingToEdit.value = binding
  editBinding.value = {
    targetEntityClass: binding.targetEntityClass,
    targetType: binding.targetType,
    targetName: binding.targetType === 'ENTITY_CLASS' ? undefined : binding.targetName || '',
  }
  showEditBindingDialog.value = true
}

async function saveBindingEdit() {
  if (!bindingToEdit.value) return
  if (editBinding.value.targetType !== 'ENTITY_CLASS' && !editBinding.value.targetName) {
    toast.error('Target element is required')
    return
  }

  try {
    const next: GlossaryBinding = {
      termId: bindingToEdit.value.termId,
      targetEntityClass: editBinding.value.targetEntityClass,
      targetType: editBinding.value.targetType,
      targetName:
        editBinding.value.targetType === 'ENTITY_CLASS'
          ? undefined
          : editBinding.value.targetName || undefined,
      created: bindingToEdit.value.created,
    }
    const currentKey = bindingKey(bindingToEdit.value)
    const nextKey = bindingKey(next)
    const duplicate = draftGlossaryBindings.value.some(
      (b) => bindingKey(b) === nextKey && bindingKey(b) !== currentKey
    )
    if (duplicate) {
      toast.error('This binding already exists')
      return
    }
    const index = draftGlossaryBindings.value.findIndex((b) => bindingKey(b) === currentKey)
    if (index === -1) {
      toast.error('Binding not found')
      return
    }
    draftGlossaryBindings.value[index] = next
    toast.success('Binding updated')
    showEditBindingDialog.value = false
    bindingToEdit.value = null
  } catch (e) {
    toast.error('Failed to update binding')
  }
}

async function handleConfirmDelete() {
  if (!bindingToDelete.value) return
  try {
    const key = bindingKey(bindingToDelete.value.binding)
    draftGlossaryBindings.value = draftGlossaryBindings.value.filter(
      (b) => bindingKey(b) !== key
    )
    toast.success('Binding deleted')
  } catch (e) {
    toast.error('Failed to delete binding')
  }
  bindingToDelete.value = null
}

function resetNewBinding() {
  newBinding.value = {
    termId: '',
    targetEntityClass: '',
    targetType: 'ENTITY_CLASS',
    targetName: undefined,
  }
}

// =============================================================================
// LLM Suggestions
// =============================================================================

async function fetchSuggestions() {
  showSuggestionsDialog.value = true
  suggestions.value = []
  selectedSuggestions.value = new Set()
  hasRequestedSuggestions.value = false
  await loadLlms()
}

async function loadLlms() {
  if (isLoadingLlms.value || availableLlms.value.length > 0) {
    return
  }
  try {
    isLoadingLlms.value = true
    availableLlms.value = await dssResourcesApi.listLlms()
    appStore.ensureDefaultLlm(availableLlms.value)
  } catch (e) {
    toast.error('Failed to load LLMs')
  } finally {
    isLoadingLlms.value = false
  }
}

async function runSuggestions() {
  if (!hasLlms.value) {
    toast.error('No LLM configured for glossary mapping')
    return
  }
  if (!selectedLlmId.value) {
    toast.error('Select an LLM')
    return
  }

  try {
    isSuggesting.value = true
    hasRequestedSuggestions.value = true
    const suggestionTerms = bindableTerms.value.filter((t) => !isTermBound(t.id))
    suggestions.value = await glossaryApi.suggestBindings(props.modelId, props.versionId, {
      spec: props.spec,
      terms: suggestionTerms,
      llm_id: selectedLlmId.value,
    })
  } catch (e) {
    toast.error('Failed to get suggestions')
  } finally {
    isSuggesting.value = false
  }
}

function toggleSuggestion(index: number) {
  if (selectedSuggestions.value.has(index)) {
    selectedSuggestions.value.delete(index)
  } else {
    selectedSuggestions.value.add(index)
  }
  // Force reactivity
  selectedSuggestions.value = new Set(selectedSuggestions.value)
}

function selectAllSuggestions() {
  selectedSuggestions.value = new Set(suggestions.value.map((_, i) => i))
}

function deselectAllSuggestions() {
  selectedSuggestions.value = new Set()
}

async function applySelectedSuggestions() {
  const toApply = [...selectedSuggestions.value].map((i) => suggestions.value[i])
  if (toApply.length === 0) return

  try {
    const bindingsToCreate: GlossaryBindingCreate[] = toApply.map((s) => ({
      termId: s.termId,
      targetEntityClass: s.targetEntityClass,
      targetType: s.targetType,
      targetName: s.targetType === 'ENTITY_CLASS' ? undefined : s.targetName,
    }))
    let created = 0
    for (const payload of bindingsToCreate) {
      const binding: GlossaryBinding = { ...payload }
      const key = bindingKey(binding)
      const exists = draftGlossaryBindings.value.some((b) => bindingKey(b) === key)
      if (!exists) {
        draftGlossaryBindings.value.push(binding)
        created += 1
      }
    }
    toast.success(`Created ${created} binding${created !== 1 ? 's' : ''}`)
    showSuggestionsDialog.value = false
  } catch (e) {
    toast.error('Failed to create bindings')
  }
}

// =============================================================================
// Vocabulary Actions
// =============================================================================

async function addToVocabulary(termId: string) {
  try {
    if (!draftVocabularyTermIds.value.includes(termId)) {
      draftVocabularyTermIds.value.push(termId)
    }
    toast.success('Term added to vocabulary')
  } catch (e) {
    toast.error('Failed to add to vocabulary')
  }
}

async function removeFromVocabulary(termId: string) {
  try {
    draftVocabularyTermIds.value = draftVocabularyTermIds.value.filter((id) => id !== termId)
    toast.success('Term removed from vocabulary')
  } catch (e) {
    toast.error('Failed to remove from vocabulary')
  }
}
</script>

<template>
  <div>
    <div v-if="isLoading" class="flex items-center justify-center p-8">
      <DkuSpinner size="lg" />
    </div>

    <div v-else>
      <!-- Bindings Subsection -->
      <div>
        <div class="flex items-center justify-end mb-3">
          <DropdownMenu>
            <DropdownMenuTrigger as-child>
              <button class="btn-tertiary">
                + Use Terms
              </button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
              <DropdownMenuItem @click="showAddBindingDialog = true">
                <i class="dku-icon-edit-16 mr-2" />
                Manually
              </DropdownMenuItem>
              <DropdownMenuItem :disabled="isSuggesting" @click="fetchSuggestions">
                <DkuSpinner v-if="isSuggesting" size="sm" class="mr-2" />
                <i v-else class="dku-icon-stars-16 mr-2" />
                AUTO
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </div>

        <div v-if="bindings.length === 0" class="text-center text-sm text-muted-foreground py-4 border rounded bg-muted/30">
          No bindings defined. Use "+ Use Terms" to add bindings manually or automatically with AI.
        </div>

        <div v-else class="border rounded overflow-hidden">
          <!-- Bulk action bar when items selected -->
          <div
            v-if="hasSelection"
            class="flex items-center gap-3 bg-primary/10 px-3 py-2 border-b"
          >
            <input
              type="checkbox"
              class="h-4 w-4 rounded border-input"
              :checked="allSelected"
              :indeterminate="someSelected"
              @change="toggleSelectAll"
            />
            <span class="text-sm font-medium">{{ selectedCount }} selected</span>
            <button
              class="text-sm font-medium text-destructive hover:underline"
              @click="handleBulkDelete"
            >
              DELETE SELECTED
            </button>
          </div>

          <table class="w-full text-sm">
            <thead v-if="!hasSelection" class="sticky top-0 z-10 bg-background">
              <tr>
                <th class="w-10 px-3 py-2">
                  <input
                    type="checkbox"
                    class="h-4 w-4 rounded border-input"
                    :checked="allSelected"
                    :indeterminate="someSelected"
                    @change="toggleSelectAll"
                  />
                </th>
                <th class="px-3 py-2 text-left font-medium">Term</th>
                <th class="px-3 py-2 text-left font-medium">Entity</th>
                <th class="px-3 py-2 text-left font-medium">Type</th>
                <th class="px-3 py-2 text-left font-medium">Target</th>
                <th class="px-3 py-2 text-right font-medium">Actions</th>
              </tr>
            </thead>
            <tbody class="divide-y">
              <tr
                v-for="binding in bindings"
                :key="bindingKey(binding)"
                :class="{ 'bg-primary/5': isBindingSelected(binding) }"
              >
                <td class="w-10 px-3 py-2">
                  <input
                    type="checkbox"
                    class="h-4 w-4 rounded border-input"
                    :checked="isBindingSelected(binding)"
                    @change="toggleBindingSelection(binding)"
                  />
                </td>
                <td class="px-3 py-2 font-medium">
                  <Tooltip>
                    <TooltipTrigger as-child>
                      <span class="cursor-default">{{ getTermName(binding.termId) }}</span>
                    </TooltipTrigger>
                    <TooltipContent class="max-w-sm">
                      <div class="space-y-1">
                        <p v-if="termsById[binding.termId]?.description" class="text-sm">
                          {{ termsById[binding.termId].description }}
                        </p>
                        <p v-if="termsById[binding.termId]?.synonyms?.length" class="text-xs text-muted-foreground">
                          <span class="font-medium">Synonyms:</span> {{ termsById[binding.termId].synonyms.join(', ') }}
                        </p>
                        <p v-if="!termsById[binding.termId]?.description && !termsById[binding.termId]?.synonyms?.length" class="text-sm text-muted-foreground italic">
                          No description or synonyms
                        </p>
                      </div>
                    </TooltipContent>
                  </Tooltip>
                </td>
                <td class="px-3 py-2">{{ getEntityName(binding.targetEntityClass) }}</td>
                <td class="px-3 py-2">{{ formatTargetType(binding.targetType) }}</td>
                <td class="px-3 py-2">{{ getTargetName(binding) }}</td>
                <td class="px-3 py-2 text-right">
                  <div class="flex items-center justify-end gap-1">
                    <Tooltip>
                      <TooltipTrigger as-child>
                        <button
                          class="rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground"
                          @click="openEditDialog(binding)"
                        >
                          <i class="dku-icon-edit-16" />
                        </button>
                      </TooltipTrigger>
                      <TooltipContent>Edit binding</TooltipContent>
                    </Tooltip>
                    <Tooltip>
                      <TooltipTrigger as-child>
                        <button
                          class="rounded p-1 text-destructive hover:bg-destructive/10"
                          @click="openDeleteDialog(binding)"
                        >
                          <i class="dku-icon-trash-16" />
                        </button>
                      </TooltipTrigger>
                      <TooltipContent>Delete binding</TooltipContent>
                    </Tooltip>
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <!-- Vocabulary Subsection -->
      <div class="p-4">
        <div class="flex items-center justify-between mb-2">
          <div class="flex items-center gap-2">
            <h3 class="text-sm font-medium">General Terms</h3>
            <span class="rounded-full bg-muted px-2 py-0.5 text-xs">
              {{ vocabularyTermIds.length }}
            </span>
          </div>
          <div class="relative">
            <div class="relative">
              <input
                v-model="vocabularySearch"
                type="text"
                placeholder="Search and add..."
                class="w-48 rounded-md border border-input bg-background px-3 py-1.5 text-sm"
                :disabled="vocabularyEligibleTerms.length === 0"
                @focus="showVocabularyDropdown = true"
                @blur="handleVocabularyBlur"
              />
              <div
                v-if="showVocabularyDropdown && filteredVocabularyTerms.length > 0"
                class="absolute top-full left-0 right-0 mt-1 max-h-48 overflow-auto rounded-md border bg-background shadow-lg z-10"
              >
                <button
                  v-for="term in filteredVocabularyTerms"
                  :key="term.id"
                  type="button"
                  class="w-full px-3 py-2 text-left text-sm hover:bg-muted"
                  @mousedown.prevent="addToVocabulary(term.id); vocabularySearch = ''"
                >
                  <span class="font-medium">{{ term.term }}</span>
                  <span v-if="term.description" class="ml-1 text-muted-foreground text-xs">- {{ term.description }}</span>
                </button>
              </div>
            </div>
          </div>
        </div>

        <p class="text-xs text-muted-foreground mb-3">
          Terms that provide general context to the LLM without mapping to specific schema elements.
          Useful for domain jargon, acronyms, or business concepts.
        </p>

        <div v-if="vocabularyTermIds.length === 0" class="text-center text-sm text-muted-foreground py-4 border rounded bg-muted/30">
          No general terms added. Search above to add glossary terms.
        </div>

        <div v-else class="space-y-2">
          <div
            v-for="termId in vocabularyTermIds"
            :key="termId"
            class="flex items-center justify-between rounded border px-3 py-2"
          >
            <div>
              <span class="font-medium">{{ termsById[termId]?.term || termId }}</span>
              <span v-if="termsById[termId]?.description" class="ml-2 text-sm text-muted-foreground">
                - {{ termsById[termId]?.description }}
              </span>
            </div>
            <button
              class="rounded p-1 text-destructive hover:bg-destructive/10"
              @click="removeFromVocabulary(termId)"
            >
              <i class="dku-icon-trash-16" />
            </button>
          </div>
        </div>
      </div>
    </div>

    <!-- Add Binding Dialog -->
    <Teleport to="body">
      <div
        v-if="showAddBindingDialog"
        class="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
      >
        <div class="w-full max-w-md rounded-lg bg-background p-6 shadow-lg">
          <h2 class="text-lg font-semibold">Add Binding</h2>
          <form class="mt-4 space-y-4" @submit.prevent="createBinding">
            <div>
              <label class="mb-1 block text-sm font-medium">Term</label>
              <div class="relative">
                <input
                  v-if="!newBinding.termId"
                  v-model="termSearch"
                  type="text"
                  placeholder="Search terms..."
                  class="w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
                  @focus="showTermDropdown = true"
                  @blur="handleTermBlur"
                />
                <div
                  v-else
                  class="flex items-center justify-between w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
                >
                  <span>{{ termsById[newBinding.termId]?.term || newBinding.termId }}</span>
                  <button
                    type="button"
                    class="text-muted-foreground hover:text-foreground"
                    @click="newBinding.termId = ''; termSearch = ''"
                  >
                    &times;
                  </button>
                </div>
                <div
                  v-if="showTermDropdown && filteredBindableTerms.length > 0"
                  class="absolute top-full left-0 right-0 mt-1 max-h-48 overflow-auto rounded-md border bg-background shadow-lg z-10"
                >
                  <button
                    v-for="term in filteredBindableTerms"
                    :key="term.id"
                    type="button"
                    class="w-full px-3 py-2 text-left text-sm hover:bg-muted"
                    @mousedown.prevent="newBinding.termId = term.id; termSearch = ''; showTermDropdown = false"
                  >
                    <span class="font-medium">{{ term.term }}</span>
                    <span v-if="term.description" class="ml-1 text-muted-foreground text-xs">- {{ term.description }}</span>
                  </button>
                </div>
              </div>
            </div>
            <div>
              <label class="mb-1 block text-sm font-medium">Entity</label>
              <SearchSelect
                :model-value="newBinding.targetEntityClass"
                :options="entityOptions"
                placeholder="Select entity..."
                search-placeholder="Search entities..."
                empty-text="No entities found"
                @update:model-value="(val: string) => newBinding.targetEntityClass = val"
              />
            </div>
            <div>
              <label class="mb-1 block text-sm font-medium">Target Type</label>
              <SearchSelect
                :model-value="newBinding.targetType"
                :options="targetTypeOptions"
                placeholder="Select target type..."
                search-placeholder="Search types..."
                empty-text="No types found"
                @update:model-value="(val: string) => newBinding.targetType = val as BindingTargetType"
              />
            </div>
            <div v-if="newBinding.targetType !== 'ENTITY_CLASS'">
              <label class="mb-1 block text-sm font-medium">Target Element</label>
              <SearchSelect
                :model-value="newBinding.targetName || ''"
                :options="availableTargetOptions"
                :placeholder="`Select ${formatTargetType(newBinding.targetType)}...`"
                :search-placeholder="`Search ${formatTargetType(newBinding.targetType)}s...`"
                empty-text="No targets found"
                @update:model-value="(val: string) => newBinding.targetName = val"
              />
            </div>
            <div class="flex justify-end gap-2">
              <button
                type="button"
                class="btn-secondary"
                @click="showAddBindingDialog = false; resetNewBinding()"
              >
                Cancel
              </button>
              <button
                type="submit"
                class="btn-primary"
              >
                OK
              </button>
            </div>
          </form>
        </div>
      </div>
    </Teleport>

    <!-- Edit Binding Dialog -->
    <Teleport to="body">
      <div
        v-if="showEditBindingDialog"
        class="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
      >
        <div class="w-full max-w-md rounded-lg bg-background p-6 shadow-lg">
          <h2 class="text-lg font-semibold">Edit Binding</h2>
          <p v-if="bindingToEdit" class="text-sm text-muted-foreground mt-1">
            Editing binding for term: <strong>{{ getTermName(bindingToEdit.termId) }}</strong>
          </p>
          <form class="mt-4 space-y-4" @submit.prevent="saveBindingEdit">
            <div>
              <label class="mb-1 block text-sm font-medium">Entity</label>
              <SearchSelect
                :model-value="editBinding.targetEntityClass"
                :options="entityOptions"
                placeholder="Select entity..."
                search-placeholder="Search entities..."
                empty-text="No entities found"
                @update:model-value="(val: string) => editBinding.targetEntityClass = val"
              />
            </div>
            <div>
              <label class="mb-1 block text-sm font-medium">Target Type</label>
              <SearchSelect
                :model-value="editBinding.targetType"
                :options="targetTypeOptions"
                placeholder="Select target type..."
                search-placeholder="Search types..."
                empty-text="No types found"
                @update:model-value="(val: string) => editBinding.targetType = val as BindingTargetType"
              />
            </div>
            <div v-if="editBinding.targetType !== 'ENTITY_CLASS'">
              <label class="mb-1 block text-sm font-medium">Target Element</label>
              <SearchSelect
                :model-value="editBinding.targetName || ''"
                :options="editAvailableTargetOptions"
                :placeholder="`Select ${formatTargetType(editBinding.targetType)}...`"
                :search-placeholder="`Search ${formatTargetType(editBinding.targetType)}s...`"
                empty-text="No targets found"
                @update:model-value="(val: string) => editBinding.targetName = val"
              />
            </div>
            <div class="flex justify-end gap-2">
              <button
                type="button"
                class="btn-secondary"
                @click="showEditBindingDialog = false; bindingToEdit = null"
              >
                Cancel
              </button>
              <button
                type="submit"
                class="btn-primary"
              >
                Save Changes
              </button>
            </div>
          </form>
        </div>
      </div>
    </Teleport>

    <!-- Suggestions Dialog -->
    <Teleport to="body">
      <div
        v-if="showSuggestionsDialog"
        class="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
      >
        <div class="w-full max-w-2xl rounded-lg bg-background p-6 shadow-lg max-h-[80vh] overflow-hidden flex flex-col">
          <h2 class="text-lg font-semibold">Suggested Usage</h2>
          <p class="text-sm text-muted-foreground mt-1">
            Select the bindings you want to create. These suggestions were generated by AI based on your terms and schema.
          </p>

          <div class="mt-4 grid gap-3 sm:grid-cols-[1fr_auto] items-end">
            <div>
              <label class="mb-1 block text-sm font-medium">LLM</label>
              <GroupedSearchSelect
                :model-value="selectedLlmId"
                :options="llmOptions"
                placeholder="Select LLM..."
                search-placeholder="Filter..."
                empty-text="No LLMs found"
                :disabled="isLoadingLlms"
                @update:model-value="(val: string) => selectedLlmId = val"
              />
              <p v-if="!hasLlms && !isLoadingLlms" class="mt-1 text-xs text-destructive">
                No LLM configured for glossary mapping.
              </p>
            </div>
            <div class="flex justify-end">
              <button
                type="button"
                class="btn-primary disabled:opacity-50"
                :disabled="isSuggesting || isLoadingLlms || !selectedLlmId"
                @click="runSuggestions"
              >
                Analyze terms
              </button>
            </div>
          </div>

          <div v-if="isSuggesting" class="flex items-center justify-center py-8">
            <DkuSpinner size="lg" />
            <span class="ml-2 text-muted-foreground">Analyzing terms...</span>
          </div>

          <div
            v-else-if="!hasRequestedSuggestions"
            class="py-8 text-center text-muted-foreground"
          >
            Select an LLM and click "Analyze terms" to generate suggestions.
          </div>

          <div v-else-if="suggestions.length === 0" class="py-8 text-center text-muted-foreground">
            No suggestions available. All approved terms may already be bound.
          </div>

          <div v-else class="flex-1 overflow-auto mt-4">
            <div class="flex items-center justify-between mb-2">
              <span class="text-sm text-muted-foreground">{{ selectedSuggestions.size }} of {{ suggestions.length }} selected</span>
              <div class="flex gap-2">
                <button class="text-sm text-primary hover:underline" @click="selectAllSuggestions">Select all</button>
                <button class="text-sm text-primary hover:underline" @click="deselectAllSuggestions">Deselect all</button>
              </div>
            </div>

            <div class="border rounded divide-y">
              <div
                v-for="(suggestion, index) in suggestions"
                :key="index"
                class="flex items-center gap-3 px-3 py-2 hover:bg-muted/50 cursor-pointer"
                @click="toggleSuggestion(index)"
              >
                <input
                  type="checkbox"
                  :checked="selectedSuggestions.has(index)"
                  class="h-4 w-4 rounded border-input"
                  @click.stop
                  @change="toggleSuggestion(index)"
                />
                <div class="flex-1">
                  <span class="font-medium">{{ getTermName(suggestion.termId) }}</span>
                  <span class="mx-2 text-muted-foreground">→</span>
                  <span>{{ getEntityName(suggestion.targetEntityClass) }}</span>
                  <span v-if="suggestion.targetType !== 'ENTITY_CLASS'" class="text-muted-foreground">
                    .{{
                      suggestion.targetType === 'ATTRIBUTE'
                        ? spec.entities
                            .find(e => e.name === suggestion.targetEntityClass)
                            ?.attributes.find(a => a.name === suggestion.targetName)?.name
                        : suggestion.targetType === 'METRIC'
                          ? spec.entities
                              .find(e => e.name === suggestion.targetEntityClass)
                              ?.metrics.find(m => m.name === suggestion.targetName)?.name
                          : spec.entities
                              .find(e => e.name === suggestion.targetEntityClass)
                              ?.filters.find(f => f.name === suggestion.targetName)?.name
                    }}
                  </span>
                </div>
              </div>
            </div>
          </div>

          <div class="flex justify-end gap-2 mt-4 pt-4 border-t">
            <button
              type="button"
              class="btn-secondary"
              @click="showSuggestionsDialog = false"
            >
              Cancel
            </button>
            <button
              type="button"
              class="btn-primary disabled:opacity-50"
              :disabled="selectedSuggestions.size === 0"
              @click="applySelectedSuggestions"
            >
              OK
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- Delete Binding Dialog -->
    <ConfirmDialog
      v-model:open="showDeleteDialog"
      title="Delete Binding"
      :description="`Are you sure you want to delete the binding for '${bindingToDelete?.termName}'?`"
      confirm-text="Delete"
      cancel-text="Cancel"
      variant="destructive"
      @confirm="handleConfirmDelete"
    />

    <!-- Bulk Delete Dialog -->
    <ConfirmDialog
      v-model:open="showBulkDeleteDialog"
      title="Delete Bindings"
      :description="`Are you sure you want to delete ${t('bindings.count', selectedCount)}?`"
      confirm-text="Delete"
      cancel-text="Cancel"
      variant="destructive"
      @confirm="confirmBulkDelete"
    />
  </div>
</template>
