<template>
  <div class="svn-fetch">
    <!-- 空状态：无预设时引导去仓库管理新增 -->
    <div v-if="!loading && !presets.length" class="card" style="text-align:center;padding:var(--space-2xl) var(--space-lg);">
      <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="color:var(--c-text-muted);margin-bottom:var(--space-md);" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
      <h3 style="font-size:16px;font-weight:600;margin-bottom:8px;color:var(--c-text);">尚未配置 SVN 仓库</h3>
      <p style="font-size:13px;color:var(--c-text-muted);margin-bottom:var(--space-lg);">
        请先在<a href="#/presets" style="color:var(--c-primary);font-weight:500;">仓库管理</a>页面新增仓库后再来抓取日志。
      </p>
      <router-link to="/presets" class="btn btn-primary">前往仓库管理</router-link>
    </div>

    <div v-else class="card">
      <div class="card-title">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
        SVN 批量抓取参数
      </div>
      <div class="alert alert-info" style="margin-bottom:var(--space-md);">
        支持多个项目批量抓取，可先选月份自动填充版本号，或手动填写。
      </div>

      <div style="padding:var(--space-md);background:rgba(15,23,42,0.4);border-radius:var(--radius-md);margin-bottom:var(--space-lg);border:1px solid var(--c-border-light);">
        <h4 style="font-size:13px;font-weight:600;margin-bottom:12px;color:var(--c-text);">智能版本号辅助</h4>
        <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
          <select v-model="rangeType" class="form-select" style="width:80px;" aria-label="范围类型">
            <option value="month">月</option>
            <option value="week">周</option>
            <option value="date">日</option>
          </select>
          <input v-if="rangeType === 'month'" type="month" v-model="dateValue" class="form-input" style="max-width:200px;" aria-label="选择月份" />
          <input v-if="rangeType === 'week'" type="week" v-model="dateValue" class="form-input" style="max-width:200px;" aria-label="选择周" />
          <input v-if="rangeType === 'date'" type="date" v-model="dateValue" class="form-input" style="max-width:200px;" aria-label="选择日期" />
          <button type="button" class="btn btn-primary" @click="autoFillVersions" :disabled="autoFillLoading">
            <span v-if="autoFillLoading" class="spinner"></span>
            自动计算并填充
          </button>
        </div>
      </div>

      <form @submit.prevent="onRunSvn">
        <div class="form-grid" style="gap:var(--space-lg);">
          <div class="span-all project-block" v-for="(proj, idx) in projects" :key="idx">
            <h4>{{ proj.name }}</h4>
            <div class="form-grid">
              <div class="form-group">
                <label :for="'start-'+idx">开始版本号</label>
                <input :id="'start-'+idx" class="form-input" v-model="proj.startRevision" inputmode="numeric" placeholder="请输入开始版本" autocomplete="off" />
              </div>
              <div class="form-group">
                <label :for="'end-'+idx">结束版本号</label>
                <input :id="'end-'+idx" class="form-input" v-model="proj.endRevision" inputmode="numeric" placeholder="请输入结束版本" autocomplete="off" />
              </div>
            </div>
          </div>

          <div class="form-group">
            <label for="filterUser">过滤用户名</label>
            <input id="filterUser" class="form-input" v-model="filterUser" placeholder="包含匹配，留空不过滤" autocomplete="off" spellcheck="false" />
          </div>
          <div class="form-group">
            <label for="period">工作周期</label>
            <input id="period" class="form-input" v-model="period" placeholder="例如 2026年03月" autocomplete="off" />
          </div>
          <div class="form-group span-all">
            <label for="outputFileName">输出文件名</label>
            <input id="outputFileName" class="form-input" v-model="outputFileName" placeholder="例如 202603工作量统计.xlsx" autocomplete="off" />
          </div>
        </div>

        <div class="btn-group" style="margin-top:var(--space-lg);">
          <button type="button" class="btn" @click="onTestConnection" :disabled="testing" aria-label="测试 SVN 连接">
            <span v-if="testing" class="spinner"></span>
            测试连接
          </button>
          <button type="submit" class="btn btn-primary" :disabled="running">
            <span v-if="running" class="spinner"></span>
            一键抓取并生成 Excel
          </button>
        </div>
      </form>
    </div>

    <div class="card" v-show="showLogPanel" style="margin-top:var(--space-lg);">
      <div class="card-title">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
        执行进度面板
      </div>
      <div class="log-pane-3">
        <div>
          <div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;color:var(--c-text-muted);margin-bottom:6px;">AI 思考过程</div>
          <div ref="reasoningPane" class="log-panel" style="height:250px;" role="log" aria-live="polite">
            <div class="log-panel-dots"><span class="dot-red"></span><span class="dot-yellow"></span><span class="dot-green"></span></div>
            <div v-if="!reasoningLines.length" class="log-line log-muted">等待思考输出...</div>
            <div v-for="(line, i) in reasoningLines" :key="i" :class="['log-line', line.cls || 'log-reasoning']">{{ line.text }}</div>
          </div>
        </div>
        <div>
          <div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;color:var(--c-text-muted);margin-bottom:6px;">最终分析输出</div>
          <div ref="answerPane" class="log-panel" style="height:250px;" role="log" aria-live="polite">
            <div class="log-panel-dots"><span class="dot-red"></span><span class="dot-yellow"></span><span class="dot-green"></span></div>
            <div v-if="!answerLines.length" class="log-line log-muted">等待答案输出...</div>
            <div v-for="(line, i) in answerLines" :key="i" :class="['log-line', line.cls || 'log-answer']">{{ line.text }}</div>
          </div>
        </div>
        <div>
          <div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;color:var(--c-text-muted);margin-bottom:6px;">系统控制台</div>
          <div ref="syslogPane" class="log-panel" style="height:180px;" role="log" aria-live="polite">
            <div class="log-panel-dots"><span class="dot-red"></span><span class="dot-yellow"></span><span class="dot-green"></span></div>
            <div v-if="!syslogLines.length" class="log-line log-muted">等待任务开始...</div>
            <div v-for="(line, i) in syslogLines" :key="i" :class="['log-line', line.cls || 'log-info']">{{ line.text }}</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { useApi, useToast } from '../composables/useApi'

const { apiFetch, downloadFile } = useApi()
const { toast } = useToast()

const presets = ref([])
const defaultPresetId = ref('')
const loading = ref(true)
const testing = ref(false)
const running = ref(false)
const autoFillLoading = ref(false)
const showLogPanel = ref(false)

const rangeType = ref('month')
const dateValue = ref('')
const filterUser = ref('liujing')
const period = ref('')
const outputFileName = ref('')
const projects = ref([])

const syslogLines = ref([])
const reasoningLines = ref([])
const answerLines = ref([])
const reasoningPane = ref(null)
const answerPane = ref(null)
const syslogPane = ref(null)

function appendSyslog(msg, isError = false) {
  const time = new Date().toLocaleTimeString('zh-CN', { hour12: false })
  syslogLines.value.push({ text: `[${time}] ${isError ? '!' : ''} ${msg}`, cls: isError ? 'log-error' : 'log-info' })
  scrollPane(syslogPane)
}
function appendReasoning(text) {
  reasoningLines.value.push({ text, cls: 'log-reasoning' })
  scrollPane(reasoningPane)
}
function appendAnswer(text) {
  answerLines.value.push({ text, cls: 'log-answer' })
  scrollPane(answerPane)
}
function clearLogs() {
  syslogLines.value = []
  reasoningLines.value = []
  answerLines.value = []
}
function scrollPane(refEl) {
  nextTick(() => { if (refEl.value) refEl.value.scrollTop = refEl.value.scrollHeight })
}

onMounted(async () => {
  try {
    const data = await apiFetch('/api/svn/presets')
    presets.value = data.presets || []
    defaultPresetId.value = data.defaultPresetId || ''
    projects.value = (data.presets || []).map(p => ({
      presetId: p.id,
      name: p.name,
      startRevision: '',
      endRevision: '',
    }))
    loading.value = false
  } catch (err) { loading.value = false; toast(err.message, true) }

  const now = new Date()
  const y = now.getFullYear()
  const m = String(now.getMonth() + 1).padStart(2, '0')
  dateValue.value = `${y}-${m}`
  period.value = `${y}年${m}月`
  outputFileName.value = `${y}${m}工作量统计.xlsx`
})

function getFormProjects() {
  return projects.value.filter(p => p.startRevision && p.endRevision).map(p => ({ ...p }))
}

async function onTestConnection() {
  if (!presets.value.length) { toast('未加载到 SVN 预设', true); return }
  testing.value = true
  try {
    const pid = defaultPresetId.value && presets.value.some(p => p.id === defaultPresetId.value)
      ? defaultPresetId.value : presets.value[0].id
    await apiFetch('/api/svn/test-connection', { method: 'POST', body: JSON.stringify({ presetId: pid }) })
    toast('SVN 连接成功')
  } catch (err) { toast(err.message, true) }
  finally { testing.value = false }
}

async function autoFillVersions() {
  if (!presets.value.length) { toast('未加载到 SVN 预设', true); return }
  const rt = rangeType.value
  const dv = dateValue.value
  if (!dv) { toast('请选择日期范围', true); return }

  let logPrefix = ''
  let body

  if (rt === 'month') {
    const [y, m] = dv.split('-')
    if (!y || !m) { toast('请选择月份', true); return }
    logPrefix = `${y}年${m}月`
    body = { presetId: '', year: parseInt(y, 10), month: parseInt(m, 10) }
  } else if (rt === 'week') {
    logPrefix = dv
    body = { presetId: '', rangeType: 'week', week: dv }
  } else {
    logPrefix = dv
    body = { presetId: '', rangeType: 'date', date: dv }
  }

  autoFillLoading.value = true
  showLogPanel.value = true
  clearLogs()
  appendSyslog(`开始查询 ${logPrefix} 的版本范围...`)
  try {
    for (let i = 0; i < presets.value.length; i++) {
      const proj = projects.value[i]
      appendSyslog(`正在查询 ${proj.name} 的版本范围...`)
      const data = await apiFetch('/api/svn/version-range', {
        method: 'POST',
        body: JSON.stringify({ ...body, presetId: proj.presetId }),
      })
      if (data.startRevision && data.endRevision) {
        proj.startRevision = String(data.startRevision)
        proj.endRevision = String(data.endRevision)
        appendSyslog(`${proj.name} 版本范围: ${data.startRevision} - ${data.endRevision}`)
      } else {
        appendSyslog(`⚠ ${proj.name} 该范围无提交记录`, true)
      }
    }
    appendSyslog('所有项目版本号填充完成')
    toast('版本号填充完成')
  } catch (err) { appendSyslog(`填充失败: ${err.message}`, true); toast(err.message, true) }
  finally { autoFillLoading.value = false }
}

async function onRunSvn() {
  if (!presets.value.length) { toast('SVN 预设加载异常', true); return }
  const formProjects = getFormProjects()
  if (!formProjects.length) { toast('请至少填写一个项目的开始和结束版本号', true); return }
  showLogPanel.value = true
  clearLogs()
  running.value = true
  appendSyslog('任务开始...')
  let aiStream = null
  try {
    const mdFiles = []
    for (let i = 0; i < formProjects.length; i++) {
      const proj = formProjects[i]
      appendSyslog(`正在提交 ${proj.name} 的抓取任务...`)
      const data = await apiFetch('/api/svn/fetch', {
        method: 'POST',
        body: JSON.stringify({ presetId: proj.presetId, startRevision: toNum(proj.startRevision), endRevision: toNum(proj.endRevision), filterUser: filterUser.value || '' }),
      })
      const taskId = data.taskId
      appendSyslog(`已创建抓取任务: ${proj.name} (${taskId.slice(0,8)})`)
      while (true) {
        const task = await apiFetch(`/api/tasks/${encodeURIComponent(taskId)}`)
        if (task.status === 'SUCCESS') {
          appendSyslog(`${proj.name} 抓取完成`)
          if (task.files) mdFiles.push(...task.files.filter(f => f.endsWith('.md')))
          break
        }
        if (task.status === 'FAILED' || task.status === 'CANCELLED') {
          throw new Error(`${proj.name} 抓取失败: ${task.error || task.message}`)
        }
        if (task.message) appendSyslog(`[${proj.name}] ${task.message}`)
        await sleep(2000)
      }
    }
    appendSyslog(`所有 SVN 抓取完成，共 ${mdFiles.length} 个文件`)
    appendSyslog('正在提交 AI 分析任务...')
    const aiData = await apiFetch('/api/ai/analyze', {
      method: 'POST',
      body: JSON.stringify({ filePaths: mdFiles, period: period.value || '', apiKey: '', outputFileName: outputFileName.value || '' }),
    })
    appendSyslog(`AI 分析任务已创建 (${aiData.taskId.slice(0,8)})`)
    const streamReady = { reasoningLen: 0, answerLen: 0 }
    const source = openStream(aiData.taskId, streamReady)
    while (true) {
      const task = await apiFetch(`/api/tasks/${encodeURIComponent(aiData.taskId)}`)
      syncAiOutput(task, streamReady)
      if (task.status === 'SUCCESS') {
        appendSyslog('AI 分析完成')
        syncAiOutput(task, streamReady)
        source?.close()
        break
      }
      if (task.status === 'FAILED' || task.status === 'CANCELLED') {
        source?.close()
        throw new Error(`AI 分析失败: ${task.error || task.message}`)
      }
      if (task.message) appendSyslog(task.message)
      await sleep(1000)
    }
    const finalTask = await apiFetch(`/api/tasks/${encodeURIComponent(aiData.taskId)}`)
    if (finalTask.files) {
      const excel = finalTask.files.find(f => f.endsWith('.xlsx'))
      if (excel) {
        appendSyslog('Excel 生成成功，开始下载...')
        await downloadFile(excel)
        appendSyslog('任务全部完成!')
      }
    }
    toast('任务全部完成')
    aiStream = source
  } catch (err) {
    appendSyslog(`错误: ${err.message}`, true)
    toast(err.message, true)
  } finally {
    if (aiStream) aiStream.close()
    running.value = false
  }
}

function openStream(taskId, state) {
  if (!window.EventSource) return { close() {} }
  const url = `/api/tasks/${encodeURIComponent(taskId)}/stream`
  let src = null
  let reconnectAttempts = 0
  const MAX_RECONNECT = 3
  let closed = false

  function connect() {
    if (closed) return
    src = new EventSource(url)
    const onEvent = (event, handler) => {
      src.addEventListener(event, e => {
        try { const d = JSON.parse(e.data); handler(d) } catch {}
      })
    }
    onEvent('reasoning_delta', d => { if (d.text) { appendReasoning(d.text); state.reasoningLen += d.text.length } })
    onEvent('answer_delta', d => { if (d.text) { appendAnswer(d.text); state.answerLen += d.text.length } })
    onEvent('phase', d => { if (d.message) appendSyslog(d.message) })
    onEvent('usage', d => { appendSyslog(`Token: prompt=${d.promptTokens || 0} / completion=${d.completionTokens || 0} / total=${d.totalTokens || 0}`) })
    onEvent('done', () => { closed = true; appendSyslog('SSE 流结束'); src.close() })

    src.onerror = () => {
      src.close()
      if (closed) return
      if (reconnectAttempts < MAX_RECONNECT) {
        reconnectAttempts++
        appendSyslog(`SSE 连接断开，${reconnectAttempts}/${MAX_RECONNECT} 次重连...`, true)
        setTimeout(connect, 2000)
      } else {
        closed = true
        appendSyslog('SSE 重连失败，切换为轮询模式', true)
      }
    }
  }

  connect()
  return { close() { closed = true; if (src) src.close() } }
}

function syncAiOutput(task, state) {
  if (!task) return
  const reasoning = task.aiReasoningText || ''
  const answer = task.aiAnswerText || ''
  if (reasoning.length > state.reasoningLen) {
    const delta = reasoning.slice(state.reasoningLen)
    if (delta) { appendReasoning(delta); state.reasoningLen = reasoning.length }
  }
  if (answer.length > state.answerLen) {
    const delta = answer.slice(state.answerLen)
    if (delta) { appendAnswer(delta); state.answerLen = answer.length }
  }
}

function toNum(v) { const n = Number(v); return Number.isFinite(n) ? n : null }
function sleep(ms) { return new Promise(r => setTimeout(r, ms)) }
</script>
