<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { toast } from 'vue-sonner'
import { glossaryApi, dssResourcesApi } from '@/api'
import { useAppStore } from '@/stores/app'
import { useSemanticModelsStore } from '@/stores/semantic-models'
import GroupedSearchSelect from '@/components/GroupedSearchSelect.vue'
import DkuSpinner from '@/components/DkuSpinner.vue'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import type { GroupedOption } from '@/components/GroupedSearchSelect.vue'
import type {
  GlossaryConflict,
  GlossaryExtractionRequest,
  GlossaryExtractionResponse,
  GlossaryTerm,
  DSSLLM,
  SemanticModelSpec,
} from '@/types'

type ConflictResolution = 'keep' | 'replace'

defineProps<{
  show: boolean
  modelId: string
  versionId: string
}>()

const emit = defineEmits<{
  close: []
}>()

const semanticModelsStore = useSemanticModelsStore()
const appStore = useAppStore()
const { selectedLlmId } = storeToRefs(appStore)

// File & LLM state
const fileInput = ref<HTMLInputElement | null>(null)
const selectedFile = ref<File | null>(null)
const availableLlms = ref<DSSLLM[]>([])

// Advanced settings
const showAdvanced = ref(false)
const maxChunks = ref(20)
const chunkSize = ref(8000)
const chunkOverlap = ref(0)

// Default values for reset
const DEFAULT_MAX_CHUNKS = 20
const DEFAULT_CHUNK_SIZE = 8000
const DEFAULT_CHUNK_OVERLAP = 0

function resetAdvancedSettings() {
  maxChunks.value = DEFAULT_MAX_CHUNKS
  chunkSize.value = DEFAULT_CHUNK_SIZE
  chunkOverlap.value = DEFAULT_CHUNK_OVERLAP
}

// Extraction state
const isExtracting = ref(false)
const extractionResult = ref<GlossaryExtractionResponse | null>(null)
const specValidationError = ref<string | null>(null)

// Preview sections collapsed state
const showNewTerms = ref(true)
const showUpdates = ref(true)
const showConflicts = ref(true)

// Conflict resolutions (termId -> resolution)
const conflictResolutions = ref<Record<string, ConflictResolution>>({})

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)

const newTerms = computed(() => extractionResult.value?.termsCreated || [])
const updatedTerms = computed(() => extractionResult.value?.termsUpdated || [])
const conflicts = computed(() => extractionResult.value?.conflicts || [])

const hasResults = computed(() => extractionResult.value !== null)
const hasConflicts = computed(() => conflicts.value.length > 0)

// Check if all conflicts are resolved
const allConflictsResolved = computed(() => {
  if (!hasConflicts.value) return true
  return conflicts.value.every(c => conflictResolutions.value[c.existing_term.id])
})

const canAddTerms = computed(() => hasResults.value && allConflictsResolved.value)

const unresolvedCount = computed(() => {
  if (!hasConflicts.value) return 0
  return conflicts.value.filter(c => !conflictResolutions.value[c.existing_term.id]).length
})

const savedSpec = computed<SemanticModelSpec | null>(() => {
  const version = semanticModelsStore.currentVersion
  if (!version) return null
  return {
    entities: version.entities || [],
    relationships: version.relationships || [],
  }
})

onMounted(async () => {
  try {
    availableLlms.value = await dssResourcesApi.listLlms()
    appStore.ensureDefaultLlm(availableLlms.value)
  } catch (e) {
    toast.error('Failed to load LLMs')
  }
})

function triggerFileSelect() {
  fileInput.value?.click()
}

function handleFileSelect(event: Event) {
  const input = event.target as HTMLInputElement
  const file = input.files?.[0]
  if (file) {
    selectedFile.value = file
    // Clear previous extraction when new file selected
    extractionResult.value = null
    conflictResolutions.value = {}
    specValidationError.value = null
  }
}

function clearSelectedFile() {
  selectedFile.value = null
  extractionResult.value = null
  conflictResolutions.value = {}
  specValidationError.value = null
  if (fileInput.value) {
    fileInput.value.value = ''
  }
}

function extractErrorMessage(error: unknown): string {
  if (error && typeof error === 'object') {
    const err = error as { response?: { data?: { error?: string } }; message?: string }
    if (err.response?.data?.error) {
      return err.response.data.error
    }
    if (err.message) {
      return err.message
    }
  }
  return 'Failed to extract terms'
}

async function handleExtract(specOverride?: SemanticModelSpec | null) {
  if (!selectedFile.value) {
    toast.error('Select a file to extract from')
    return
  }
  const specToUse = specOverride || semanticModelsStore.draftSpec
  if (!specToUse) {
    toast.error('Semantic model spec is not loaded')
    return
  }
  if (hasLlms.value && !selectedLlmId.value) {
    toast.error('Select an LLM')
    return
  }
  if (!hasLlms.value) {
    toast.error('No LLM configured for extraction')
    return
  }
  if (maxChunks.value <= 0) {
    toast.error('Max chunks must be greater than 0')
    return
  }
  if (chunkSize.value <= 0) {
    toast.error('Chunk size must be greater than 0')
    return
  }
  if (chunkOverlap.value < 0) {
    toast.error('Chunk overlap must be 0 or greater')
    return
  }
  if (chunkOverlap.value >= chunkSize.value) {
    toast.error('Chunk overlap must be smaller than chunk size')
    return
  }

  isExtracting.value = true
  conflictResolutions.value = {}
  specValidationError.value = null

  try {
    const payload: GlossaryExtractionRequest = {
      spec: specToUse,
      terms: semanticModelsStore.draftGlossaryTerms,
      llm_id: selectedLlmId.value || undefined,
      max_chunks: maxChunks.value,
      chunk_size: chunkSize.value,
      chunk_overlap: chunkOverlap.value,
    }

    extractionResult.value = await glossaryApi.extractFromFile(selectedFile.value, payload)

    const created = extractionResult.value.termsCreated.length
    const updated = extractionResult.value.termsUpdated.length
    const conflictCount = extractionResult.value.conflicts.length

    if (conflictCount > 0) {
      toast.info(`Extracted ${created + updated} terms with ${conflictCount} conflict${conflictCount !== 1 ? 's' : ''} to resolve`)
    } else {
      toast.success(`Extracted ${created} new, ${updated} updated terms`)
    }
  } catch (e) {
    const message = extractErrorMessage(e)
    if (!specOverride && message.startsWith('Invalid spec payload')) {
      specValidationError.value = message
      return
    }
    toast.error(message)
    extractionResult.value = null
  } finally {
    isExtracting.value = false
  }
}

async function handleExtractWithSavedSpec() {
  if (!savedSpec.value) {
    toast.error('No saved version available')
    return
  }
  await handleExtract(savedSpec.value)
}

function resolveConflict(termId: string, resolution: ConflictResolution) {
  conflictResolutions.value = { ...conflictResolutions.value, [termId]: resolution }
}

function handleAddTerms() {
  if (!extractionResult.value) return

  const result = extractionResult.value
  const now = new Date().toISOString()

  // Build map of updated terms
  const updatedMap = new Map(result.termsUpdated.map((term) => [term.id, term]))

  // Build map of conflict resolutions that chose 'replace'
  const conflictReplaceMap = new Map<string, GlossaryConflict>()
  for (const conflict of result.conflicts) {
    const resolution = conflictResolutions.value[conflict.existing_term.id]
    if (resolution === 'replace') {
      conflictReplaceMap.set(conflict.existing_term.id, conflict)
    }
  }

  // Update existing terms
  const nextTerms: GlossaryTerm[] = semanticModelsStore.draftGlossaryTerms.map((term) => {
    // Check if this term has a conflict resolution to replace
    const conflict = conflictReplaceMap.get(term.id)
    if (conflict) {
      const extracted = conflict.extracted_term
      return {
        ...term,
        term: extracted.term || term.term,
        description: extracted.description ?? term.description,
        synonyms: extracted.synonyms ?? term.synonyms,
        privateEditorData: { ...(term.privateEditorData || {}), ...(extracted.privateEditorData || {}) },
        source: 'EXTRACTED' as const,
        userModified: false,
        modified: { on: now },
      }
    }
    // Check if this term was updated (no conflict)
    return updatedMap.get(term.id) || term
  })

  // Add new terms
  const seen = new Set(nextTerms.map((t) => t.id))
  for (const term of result.termsCreated) {
    if (!seen.has(term.id)) {
      nextTerms.push(term)
      seen.add(term.id)
    }
  }

  semanticModelsStore.draftGlossaryTerms = nextTerms

  const total = result.termsCreated.length + result.termsUpdated.length + conflictReplaceMap.size
  toast.success(`Added ${total} term${total !== 1 ? 's' : ''} to glossary`)
  handleClose()
}

function handleClose() {
  selectedFile.value = null
  extractionResult.value = null
  conflictResolutions.value = {}
  specValidationError.value = null
  if (fileInput.value) {
    fileInput.value.value = ''
  }
  emit('close')
}
</script>

<template>
  <Teleport to="body">
    <div
      v-if="show"
      class="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
    >
      <div class="flex max-h-[85vh] w-full max-w-2xl flex-col rounded-lg bg-background shadow-lg">
        <!-- Fixed Header -->
        <div class="border-b px-6 py-4">
          <h2 class="text-lg font-semibold">Extract Terms from Document</h2>
          <p class="text-sm text-muted-foreground">
            Upload a document to extract glossary terms using AI
          </p>
        </div>

        <!-- Scrollable Content -->
        <div class="flex-1 overflow-y-auto px-6 py-4">
          <div class="space-y-4">
            <!-- File Input -->
            <input
              ref="fileInput"
              type="file"
              class="hidden"
              accept=".txt,.md,.pdf,.doc,.docx,.rtf,.csv,.json,.pptx"
              @change="handleFileSelect"
            />

            <div class="flex flex-wrap items-center gap-3">
              <button
                class="btn-secondary inline-flex items-center gap-2"
                @click="triggerFileSelect"
              >
                <i class="dku-icon-file-upload-16" />
                Choose File
              </button>

              <div v-if="selectedFile" class="flex items-center gap-2 text-sm">
                <span class="font-medium">{{ selectedFile.name }}</span>
                <span class="text-muted-foreground">
                  ({{ Math.ceil(selectedFile.size / 1024) }} KB)
                </span>
                <button
                  class="rounded p-1 text-muted-foreground hover:bg-muted"
                  @click="clearSelectedFile"
                >
                  <i class="dku-icon-dismiss-16" />
                </button>
              </div>
            </div>

            <!-- LLM Selection -->
            <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"
                @update:model-value="(val: string) => selectedLlmId = val"
              />
            </div>

            <!-- Advanced Settings -->
            <div>
              <button
                class="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground"
                type="button"
                @click="showAdvanced = !showAdvanced"
              >
                <i :class="showAdvanced ? 'dku-icon-chevron-down-16' : 'dku-icon-chevron-right-16'" />
                Advanced settings
              </button>

              <div v-if="showAdvanced" class="mt-3 space-y-3">
                <div class="grid gap-3 sm:grid-cols-3">
                  <div>
                    <label class="mb-1 flex items-center gap-1 text-xs font-medium text-muted-foreground">
                      Max chunks
                      <TooltipProvider>
                        <Tooltip>
                          <TooltipTrigger as-child>
                            <i class="dku-icon-info-circle-fill-16 text-muted-foreground" />
                          </TooltipTrigger>
                          <TooltipContent side="top" class="max-w-64">
                            <p>Maximum number of document sections to process. Higher values extract more terms but increase LLM costs and processing time. Each chunk is sent as a separate LLM request.</p>
                          </TooltipContent>
                        </Tooltip>
                      </TooltipProvider>
                    </label>
                    <input
                      v-model.number="maxChunks"
                      type="number"
                      min="1"
                      class="h-9 w-full rounded-md border bg-background px-3 text-sm"
                    />
                  </div>
                  <div>
                    <label class="mb-1 flex items-center gap-1 text-xs font-medium text-muted-foreground">
                      Chunk size
                      <TooltipProvider>
                        <Tooltip>
                          <TooltipTrigger as-child>
                            <i class="dku-icon-info-circle-fill-16 text-muted-foreground" />
                          </TooltipTrigger>
                          <TooltipContent side="top" class="max-w-64">
                            <p>Size of each document section in characters. Larger chunks provide more context but may hit LLM token limits. Smaller chunks are faster but may miss terms that span sections.</p>
                          </TooltipContent>
                        </Tooltip>
                      </TooltipProvider>
                    </label>
                    <input
                      v-model.number="chunkSize"
                      type="number"
                      min="500"
                      step="100"
                      class="h-9 w-full rounded-md border bg-background px-3 text-sm"
                    />
                  </div>
                  <div>
                    <label class="mb-1 flex items-center gap-1 text-xs font-medium text-muted-foreground">
                      Chunk overlap
                      <TooltipProvider>
                        <Tooltip>
                          <TooltipTrigger as-child>
                            <i class="dku-icon-info-circle-fill-16 text-muted-foreground" />
                          </TooltipTrigger>
                          <TooltipContent side="top" class="max-w-64">
                            <p>Number of characters to overlap between consecutive chunks. Overlap helps capture terms that appear at chunk boundaries. Set to 0 for no overlap, or increase if terms are being missed.</p>
                          </TooltipContent>
                        </Tooltip>
                      </TooltipProvider>
                    </label>
                    <input
                      v-model.number="chunkOverlap"
                      type="number"
                      min="0"
                      step="50"
                      class="h-9 w-full rounded-md border bg-background px-3 text-sm"
                    />
                  </div>
                </div>
                <button
                  type="button"
                  class="text-xs text-muted-foreground hover:text-foreground hover:underline"
                  @click="resetAdvancedSettings"
                >
                  Reset to defaults
                </button>
              </div>
            </div>

            <!-- Extraction Results Preview -->
            <div v-if="hasResults" class="space-y-3 border-t pt-4">
              <h3 class="text-sm font-semibold">Extraction Results</h3>

              <!-- New Terms Section -->
              <div v-if="newTerms.length > 0">
                <button
                  class="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground"
                  type="button"
                  @click="showNewTerms = !showNewTerms"
                >
                  <i :class="showNewTerms ? 'dku-icon-chevron-down-16' : 'dku-icon-chevron-right-16'" />
                  New terms ({{ newTerms.length }})
                </button>
                <div v-if="showNewTerms" class="mt-2 max-h-40 overflow-y-auto rounded-md border">
                  <div
                    v-for="(term, index) in newTerms"
                    :key="term.id"
                    class="px-3 py-2"
                    :class="index % 2 === 1 ? 'bg-muted/50' : ''"
                  >
                    <span class="text-sm font-medium">{{ term.term }}</span>
                    <p v-if="term.description" class="text-xs text-muted-foreground line-clamp-1">
                      {{ term.description }}
                    </p>
                  </div>
                </div>
              </div>

              <!-- Updated Terms Section -->
              <div v-if="updatedTerms.length > 0">
                <button
                  class="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground"
                  type="button"
                  @click="showUpdates = !showUpdates"
                >
                  <i :class="showUpdates ? 'dku-icon-chevron-down-16' : 'dku-icon-chevron-right-16'" />
                  Updates ({{ updatedTerms.length }})
                </button>
                <div v-if="showUpdates" class="mt-2 max-h-40 overflow-y-auto rounded-md border">
                  <div
                    v-for="(term, index) in updatedTerms"
                    :key="term.id"
                    class="px-3 py-2"
                    :class="index % 2 === 1 ? 'bg-muted/50' : ''"
                  >
                    <span class="text-sm font-medium">{{ term.term }}</span>
                    <p v-if="term.description" class="text-xs text-muted-foreground line-clamp-1">
                      {{ term.description }}
                    </p>
                  </div>
                </div>
              </div>

              <!-- Conflicts Section -->
              <div v-if="hasConflicts">
                <button
                  class="flex items-center gap-2 text-sm text-amber-700 hover:text-amber-800 dark:text-amber-400 dark:hover:text-amber-300"
                  type="button"
                  @click="showConflicts = !showConflicts"
                >
                  <i :class="showConflicts ? 'dku-icon-chevron-down-16' : 'dku-icon-chevron-right-16'" />
                  <i class="dku-icon-warning-16" />
                  Conflicts ({{ unresolvedCount }} of {{ conflicts.length }} unresolved)
                </button>
                <div v-if="showConflicts" class="mt-2 rounded-md border border-amber-300 dark:border-amber-700">
                  <p class="bg-amber-50 px-3 py-2 text-xs text-amber-700 dark:bg-amber-900/20 dark:text-amber-400">
                    These terms already exist. Click on a definition to keep the existing one or replace with the extracted one.
                  </p>
                  <div class="max-h-64 overflow-y-auto">
                    <div
                      v-for="(conflict, index) in conflicts"
                      :key="conflict.existing_term.id"
                      class="p-3"
                      :class="index % 2 === 1 ? 'bg-muted/30' : ''"
                    >
                      <p class="mb-2 font-semibold">{{ conflict.existing_term.term }}</p>

                      <div class="grid gap-2 text-sm sm:grid-cols-2">
                        <button
                          type="button"
                          class="rounded border-2 p-2 text-left transition-all"
                          :class="conflictResolutions[conflict.existing_term.id] === 'keep'
                            ? 'border-primary'
                            : conflictResolutions[conflict.existing_term.id] === 'replace'
                              ? 'border-transparent opacity-50'
                              : 'border-transparent hover:border-muted-foreground/30'"
                          @click="resolveConflict(conflict.existing_term.id, 'keep')"
                        >
                          <p class="mb-1 text-xs font-medium text-muted-foreground">Existing</p>
                          <p class="text-xs line-clamp-3">{{ conflict.existing_term.description || '(no description)' }}</p>
                        </button>
                        <button
                          type="button"
                          class="rounded border-2 p-2 text-left transition-all"
                          :class="conflictResolutions[conflict.existing_term.id] === 'replace'
                            ? 'border-primary'
                            : conflictResolutions[conflict.existing_term.id] === 'keep'
                              ? 'border-transparent opacity-50'
                              : 'border-transparent hover:border-muted-foreground/30'"
                          @click="resolveConflict(conflict.existing_term.id, 'replace')"
                        >
                          <p class="mb-1 text-xs font-medium text-muted-foreground">Extracted</p>
                          <p class="text-xs line-clamp-3">{{ conflict.extracted_term.description || '(no description)' }}</p>
                        </button>
                      </div>
                    </div>
                  </div>
                </div>
              </div>

              <!-- No results message -->
              <div
                v-if="newTerms.length === 0 && updatedTerms.length === 0 && conflicts.length === 0"
                class="rounded-md border border-dashed p-4 text-center text-sm text-muted-foreground"
              >
                No terms were extracted from the document.
              </div>
            </div>
          </div>
        </div>

        <div
          v-if="specValidationError"
          class="mx-6 mb-3 rounded-md border border-amber-500/50 bg-amber-50 px-4 py-3 text-sm text-amber-800 dark:border-amber-700/60 dark:bg-amber-950/20 dark:text-amber-300"
        >
          <div class="flex items-start gap-2">
            <i class="dku-icon-warning-outline-16 mt-0.5 text-amber-600 dark:text-amber-400" />
            <div class="flex-1">
              <p class="font-medium">Your current model version has unsaved changes with errors.</p>
              <p class="text-xs text-amber-700/80 dark:text-amber-300/80">
                You can continue with the last saved version instead.
              </p>
            </div>
            <button
              type="button"
              class="btn-tertiary h-8"
              @click="handleExtractWithSavedSpec"
              :disabled="!savedSpec"
            >
              Use last saved version
            </button>
          </div>
        </div>

        <!-- Fixed Footer -->
        <div class="flex items-center justify-between border-t px-6 py-4">
          <div class="text-sm text-muted-foreground">
            <span v-if="hasConflicts && !allConflictsResolved">
              Resolve all conflicts to add terms
            </span>
          </div>
          <div class="flex gap-2">
            <button
              type="button"
              class="btn-secondary"
              @click="handleClose"
            >
              Cancel
            </button>
            <!-- Extract button (before extraction) -->
            <button
              v-if="!hasResults"
              type="button"
              class="btn-primary inline-flex items-center gap-2 disabled:opacity-50"
              :disabled="!selectedFile || isExtracting"
              @click="handleExtract()"
            >
              <DkuSpinner v-if="isExtracting" size="md" />
              <i v-else class="dku-icon-stars-16" />
              Extract
            </button>
            <!-- Add Terms button (after extraction) -->
            <button
              v-else
              type="button"
              class="btn-primary disabled:opacity-50"
              :disabled="!canAddTerms"
              @click="handleAddTerms"
            >
              Add Terms
            </button>
          </div>
        </div>
      </div>
    </div>
  </Teleport>
</template>
