<!-- =====================================================================
  ImportModal.vue — 本地资料导入对话框
  支持：图片直接插入，文档（PDF/DOCX/MD/TXT）→ LLM 分析生成幻灯片
  ===================================================================== -->
<script setup lang="ts">
import { ref, computed } from 'vue'
import { store } from '../../core/store'
import {
  scanFiles, readEntries,
  aiAnalyzeDocument,
  fileToImageElement, imagesToSlideElements,
  type FileEntry, type ImportReport,
  describeReport
} from '../../core/importer'

const props = withDefaults(defineProps<{ visible: boolean }>(), { visible: false })
const emit = defineEmits<{
  (e: 'close'): void
  (e: 'toast', msg: string): void
  (e: 'open-settings'): void
}>()

/* ---------- 状态 ---------- */
const activeTab = ref<'files' | 'dir'>('files')
const entries = ref<FileEntry[]>([])
const loading = ref(false)
const loaded = ref(false)
const analyzing = ref(false)        // AI 分析中
const analyzeDone = ref(false)      // AI 分析完成
const analyzeError = ref('')        // AI 分析错误
const showImages = ref(true)
const showDocs = ref(true)

/* ---------- 过滤后的文件 ---------- */
const filteredEntries = computed(() => {
  return entries.value.filter(e => {
    if (e.kind === 'image' && !showImages.value) return false
    if (e.kind === 'document' && !showDocs.value) return false
    return true
  })
})

const imageEntries = computed(() => filteredEntries.value.filter(e => e.kind === 'image'))
const docEntries = computed(() => filteredEntries.value.filter(e => e.kind === 'document'))
const unsupportedCount = computed(() => entries.value.filter(e => e.kind === 'unsupported').length)

/** 是否有文档需要 AI 分析 */
const hasDocs = computed(() => docEntries.value.length > 0)
/** AI 是否已配置 */
const aiReady = computed(() => !!store.getCfg().key)

/* ---------- 打开文件选择器 ---------- */
let fileInput: HTMLInputElement | null = null
let dirInput: HTMLInputElement | null = null

function openFilePicker() {
  if (!fileInput) {
    fileInput = document.createElement('input')
    fileInput.type = 'file'
    fileInput.multiple = true
    fileInput.accept = '.png,.jpg,.jpeg,.gif,.webp,.svg,.md,.markdown,.txt,.text,.pdf,.docx,.doc'
    fileInput.onchange = () => handleFiles(fileInput!.files)
  }
  fileInput.value = ''
  fileInput.click()
}

function openDirPicker() {
  if (!dirInput) {
    dirInput = document.createElement('input')
    dirInput.type = 'file'
    ;(dirInput as any).webkitdirectory = true
    dirInput.onchange = () => handleFiles(dirInput!.files)
  }
  dirInput.value = ''
  dirInput.click()
}

async function handleFiles(fl: FileList | null) {
  if (!fl || fl.length === 0) return
  loading.value = true
  loaded.value = false
  analyzeDone.value = false
  analyzeError.value = ''
  try {
    const scanned = await scanFiles(fl)
    entries.value = scanned
    // 读取文件原始内容
    if (scanned.some(e => e.kind === 'image' || e.kind === 'document')) {
      await readEntries(scanned)
    }
    loaded.value = true
  } catch (e: any) {
    emit('toast', '读取失败: ' + (e?.message || String(e)))
  } finally {
    loading.value = false
  }
}

/* ---------- AI 分析文档 ---------- */
async function runAiAnalysis() {
  const docs = docEntries.value.filter(d => d.data && !d.slides)
  if (docs.length === 0) return

  if (!aiReady.value) {
    analyzeError.value = '请先在 ⚙ 设置中配置 AI API Key'
    return
  }

  analyzing.value = true
  analyzeError.value = ''
  let successCount = 0

  for (const doc of docs) {
    analyzeError.value = `正在 AI 分析（${successCount + 1}/${docs.length}）：${doc.name}`
    try {
      await aiAnalyzeDocument(doc)
      successCount++
    } catch (e: any) {
      const msg = e?.message || String(e)
      doc.error = msg
      analyzeError.value = `「${doc.name}」分析失败: ${msg}`
      console.warn(`AI 分析失败 ${doc.name}:`, e)
    }
  }

  analyzing.value = false
  analyzeDone.value = successCount > 0
  if (successCount > 0 && !analyzeError.value) {
    emit('toast', `AI 完成 ${successCount} 个文档分析，共生成 ${docs.reduce((s, d) => s + (d.slides?.length || 0), 0)} 页幻灯片`)
  }
}

/* ---------- 执行导入 ---------- */
function doImport() {
  const images = imageEntries.value.filter(e => e.data)
  const docs = docEntries.value.filter(e => e.slides && e.slides.length > 0)

  if (images.length === 0 && docs.length === 0) {
    // 如果有文档但还没 AI 分析，先分析
    if (docEntries.value.filter(d => d.data && !d.slides).length > 0) {
      void runAiAnalysis()  // fire-and-forget：分析完成后 UI 自动更新按钮
      return
    }
    emit('toast', '没有可导入的内容')
    return
  }

  let insertedImages = 0
  let insertedSlides = 0

  // 图片体积闸门：localStorage 约 5MB 字符上限，超限导入后刷新会丢图
  if (images.length > 0) {
    const imgChars = images.reduce((s, e) => s + (e.data?.length || 0), 0)
    let deckChars = 0
    try { deckChars = JSON.stringify(store.getDeck()).length } catch (e) { /* ignore */ }
    const QUOTA_CHARS = 4_500_000
    if (deckChars + imgChars > QUOTA_CHARS) {
      emit('toast', '图片过大：导入后约 ' + ((deckChars + imgChars) / 1048576).toFixed(1) + 'MB，超本地存储上限（约 5MB），刷新可能丢失。请压缩图片、减少数量，或先清理文库/旧图')
      return
    }
  }

  // 1. 图片 → 当前幻灯片
  if (images.length > 0) {
    if (images.length === 1) {
      const el = fileToImageElement(images[0].file, images[0].data!)
      store.addElement('image', { content: el.content, x: el.x, y: el.y, w: el.w, h: el.h, style: el.style })
      insertedImages = 1
    } else {
      const els = imagesToSlideElements(images.map(f => ({ file: f.file, dataUrl: f.data! })))
      for (const el of els) {
        store.addElement('image', { content: el.content, x: el.x, y: el.y, w: el.w, h: el.h, style: el.style })
        insertedImages++
      }
    }
  }

  // 2. AI 生成的幻灯片 → 追加到 deck
  if (docs.length > 0) {
    for (const d of docs) {
      if (d.slides) {
        for (const slide of d.slides) {
          store.appendSlide(slide)
          insertedSlides++
        }
      }
    }
  }

  const report: ImportReport = { images: insertedImages, slides: insertedSlides, files: entries.value.length }
  emit('toast', describeReport(report))
  emit('close')
}

/* ---------- 清除 ---------- */
function clearFiles() {
  entries.value = []
  loaded.value = false
  analyzeDone.value = false
  analyzeError.value = ''
}

/* ---------- 格式友好文件大小 ---------- */
function fmtSize(bytes: number): string {
  if (bytes < 1024) return bytes + ' B'
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
  return (bytes / 1024 / 1024).toFixed(1) + ' MB'
}

/** 截取文本预览前一段 */
function textPreview(data: string, maxLen = 80): string {
  return data.replace(/\n/g, ' ').slice(0, maxLen) + (data.length > maxLen ? '…' : '')
}
</script>

<template>
  <div v-if="props.visible" class="modal-mask" @click.self="emit('close')">
    <div class="modal import-modal">
      <header class="modal-header">
        <h2>📂 导入本地资料</h2>
        <button class="close" @click="emit('close')">✕</button>
      </header>

      <!-- ======== 选择区域 ======== -->
      <div v-if="!loaded" class="import-select">
        <div class="import-tabs">
          <button class="tab" :class="{ active: activeTab === 'files' }" @click="activeTab = 'files'">📄 选择文件</button>
          <button class="tab" :class="{ active: activeTab === 'dir' }" @click="activeTab = 'dir'">📁 读取目录</button>
        </div>

        <div v-if="activeTab === 'files'" class="dropzone" @click="openFilePicker">
          <div class="dropzone-icon">📄</div>
          <div class="dropzone-text">
            <strong>点击选择文件</strong>
            <span class="hint">图片直接插入 · 文档（PDF/DOCX/MD/TXT）由 AI 分析生成幻灯片</span>
          </div>
        </div>

        <div v-else class="dropzone" @click="openDirPicker">
          <div class="dropzone-icon">📁</div>
          <div class="dropzone-text">
            <strong>点击选择目录</strong>
            <span class="hint">读取目录下所有支持的图片和文档，AI 自动分析生成</span>
          </div>
        </div>

        <div v-if="loading" class="loading">
          <span class="spin">⟳</span> 正在读取文件...
        </div>
      </div>

      <!-- ======== 预览区 ======== -->
      <div v-else class="import-preview">
        <div class="preview-actions">
          <span class="file-count">
            已选 {{ filteredEntries.length }} 个文件
            <template v-if="unsupportedCount > 0">（{{ unsupportedCount }} 个不支持的类型已忽略）</template>
          </span>
          <button class="btn-sm" @click="clearFiles">重新选择</button>
        </div>

        <!-- 过滤 -->
        <div class="filter-bar">
          <label class="chk"><input type="checkbox" v-model="showImages" /> 🖼 图片 ({{ imageEntries.length }})</label>
          <label class="chk"><input type="checkbox" v-model="showDocs" /> 📄 文档 ({{ docEntries.length }})</label>
        </div>

        <!-- AI 分析进度 -->
        <div v-if="analyzing" class="ai-progress">
          <span class="spin">⟳</span> AI 正在分析文档内容，理解语义并生成幻灯片…
        </div>

        <!-- AI 分析错误 -->
        <div v-if="analyzeError && !analyzing" class="ai-error">
          ⚠ {{ analyzeError }}
          <template v-if="!aiReady">
            <button class="btn-sm" @click="emit('open-settings')">去配置</button>
          </template>
        </div>

        <!-- 文件列表 -->
        <div class="file-list">
          <div v-for="(entry, i) in filteredEntries" :key="i" class="file-item" :class="entry.kind">
            <span class="file-icon">{{ entry.kind === 'image' ? '🖼' : '📄' }}</span>
            <span class="file-name" :title="entry.name">{{ entry.name }}</span>
            <span class="file-size">{{ fmtSize(entry.file.size) }}</span>
            <span class="file-status">
              <!-- 图片 -->
              <template v-if="entry.kind === 'image' && entry.data">✅ 图片就绪</template>
              <!-- 文档：AI 分析结果 -->
              <template v-else-if="entry.kind === 'document' && entry.slides">✅ AI → {{ entry.slides.length }} 页</template>
              <template v-else-if="entry.kind === 'document' && entry.data && analyzeDone">⏳ 待分析</template>
              <template v-else-if="entry.kind === 'document' && entry.data && analyzing">⟳ AI 分析中</template>
              <template v-else-if="entry.kind === 'document' && entry.data">📄 {{ textPreview(entry.data) }}</template>
              <template v-else-if="entry.error">⚠ {{ entry.error }}</template>
              <template v-else-if="entry.kind === 'unsupported'">⏭ 跳过</template>
              <template v-else>⏳ 读取中</template>
            </span>
          </div>
        </div>

        <!-- 文档幻灯片预览 -->
        <div v-if="docEntries.filter(e => e.slides).length > 0" class="preview-slides">
          <span class="preview-title">🤖 AI 解析结果</span>
          <div class="preview-scroll">
            <div v-for="(entry, i) in docEntries.filter(e => e.slides)" :key="'p' + i" class="preview-item">
              <strong>{{ entry.name }}</strong>
              → {{ entry.slides?.length }} 页幻灯片
              <span class="preview-titles">
                {{ entry.slides?.map(s => {
                  const t = s.elements.find(e => e.type === 'title')
                  return t ? t.content.slice(0, 20) : '无标题'
                }).join(' · ') }}
              </span>
            </div>
          </div>
        </div>

        <!-- 操作按钮 -->
        <div class="import-actions">
          <button class="btn primary" @click="doImport" :disabled="filteredEntries.length === 0 || analyzing">
            {{ hasDocs && !analyzeDone ? '🤖 AI 分析文档并导入' : '✅ 导入以上内容' }}
          </button>
          <button class="btn" @click="emit('close')">取消</button>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.import-modal { max-width: 660px; max-height: 85vh; display: flex; flex-direction: column; }
.import-select { padding: 8px 0; }
.import-tabs { display: flex; gap: 4px; margin-bottom: 16px; }
.import-tabs .tab {
  flex: 1; padding: 8px 16px; border: 1px solid var(--border, #e2e8f0);
  background: var(--bg, #fff); border-radius: 8px; cursor: pointer;
  font-size: 14px; transition: all .15s;
}
.import-tabs .tab.active { background: var(--primary, #4f46e5); color: #fff; border-color: var(--primary, #4f46e5); }
.dropzone {
  border: 2px dashed var(--border, #cbd5e1); border-radius: 12px; padding: 36px 24px;
  display: flex; align-items: center; gap: 20px; cursor: pointer; transition: all .2s;
  background: var(--panel, #f8fafc);
}
.dropzone:hover { border-color: var(--primary, #4f46e5); background: color-mix(in srgb, var(--primary, #4f46e5) 5%, var(--panel, #f8fafc)); }
.dropzone-icon { font-size: 44px; }
.dropzone-text { display: flex; flex-direction: column; gap: 4px; }
.dropzone-text strong { font-size: 16px; }
.hint { font-size: 13px; color: var(--muted, #64748b); }

.import-preview { display: flex; flex-direction: column; gap: 8px; overflow: hidden; }
.preview-actions { display: flex; justify-content: space-between; align-items: center; }
.file-count { font-size: 14px; color: var(--muted, #64748b); }
.btn-sm { padding: 4px 12px; font-size: 13px; border: 1px solid var(--border, #e2e8f0); border-radius: 6px; background: var(--bg, #fff); cursor: pointer; }

.filter-bar { display: flex; gap: 16px; padding: 4px 0; }
.filter-bar .chk { display: flex; align-items: center; gap: 6px; font-size: 14px; cursor: pointer; }

.ai-progress { padding: 6px 12px; background: rgba(79, 70, 229, 0.06); border-radius: 8px; font-size: 13px; color: var(--primary, #4f46e5); }
.ai-error { padding: 6px 12px; background: rgba(239, 68, 68, 0.06); border-radius: 8px; font-size: 13px; color: #dc2626; display: flex; align-items: center; gap: 8px; }

.file-list { max-height: 200px; overflow-y: auto; border: 1px solid var(--border, #e2e8f0); border-radius: 8px; display: flex; flex-direction: column; }
.file-item { display: flex; align-items: center; gap: 8px; padding: 5px 10px; font-size: 13px; border-bottom: 1px solid var(--border, #f1f5f9); }
.file-item:last-child { border-bottom: none; }
.file-item.document { background: rgba(5, 150, 105, 0.04); }
.file-item.unsupported { opacity: 0.5; }
.file-icon { font-size: 16px; width: 24px; text-align: center; }
.file-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-size { color: var(--muted, #64748b); min-width: 56px; text-align: right; }
.file-status { min-width: 80px; text-align: right; font-size: 12px; }

.preview-slides { font-size: 13px; }
.preview-title { font-weight: 600; display: block; margin-bottom: 4px; }
.preview-scroll { max-height: 100px; overflow-y: auto; }
.preview-item { padding: 3px 0; }
.preview-titles { display: block; font-size: 12px; color: var(--muted, #64748b); margin-top: 1px; }

.import-actions { display: flex; gap: 8px; justify-content: flex-end; padding-top: 8px; border-top: 1px solid var(--border, #e2e8f0); }

.loading { text-align: center; padding: 24px; color: var(--muted, #64748b); font-size: 14px; }
.spin { display: inline-block; animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
</style>
