<template>
  <PermissionGuard widget-id="ConfigCenter">
    <div class="logview">
      <!-- ── 顶部概览 ── -->
      <el-card shadow="never" class="ov-card">
        <div class="ov-head">
          <div class="ov-title">
            <el-icon><Document /></el-icon>
            <span>日志管理</span>
            <el-tag size="small" effect="plain" type="info">Diagnosis · Post / Toast / DLog</el-tag>
          </div>
          <el-button size="small" :icon="Refresh" :loading="overviewLoading" @click="refreshAll">刷新</el-button>
        </div>
        <p class="ov-desc">
          <b>实时诊断</b>直连 SimpleLite 内核 <code>Diagnosis</code> 的内存态 <code>Post</code> / <code>Toast</code>
          （有标签按标签合订、无标签滚动记录）；<b>日志文件</b>浏览工作目录 <code>log/</code> 下的落盘日志（DLog）。
        </p>
        <div v-if="overview" class="ov-stats">
          <div class="ov-stat"><span class="k">日志根目录</span><span class="v path" :title="overview.root ?? ''">{{ overview.root ?? '—' }}</span></div>
          <div class="ov-stat"><span class="k">文件数</span><span class="v">{{ overview.totalFiles ?? 0 }}</span></div>
          <div class="ov-stat"><span class="k">总大小</span><span class="v">{{ formatBytes(overview.totalBytes ?? 0) }}</span></div>
          <div class="ov-stat"><span class="k">最近写入</span><span class="v">{{ overview.latestFileTime ? formatTime(overview.latestFileTime) : '—' }}</span></div>
        </div>
      </el-card>

      <!-- ── 主体两视图 ── -->
      <el-card shadow="never" class="body-card" body-style="padding: 0 14px 14px">
        <el-tabs v-model="activeTab" class="log-tabs">
          <!-- 实时诊断 -->
          <el-tab-pane name="live">
            <template #label><el-icon><DataLine /></el-icon><span class="tab-lbl">实时诊断</span></template>

            <div class="toolbar">
              <el-switch v-model="autoRefresh" size="small" inline-prompt active-text="自动" inactive-text="手动" />
              <el-select v-model="intervalSec" size="small" class="sel-interval" :disabled="!autoRefresh">
                <el-option :value="1" label="每 1 秒" />
                <el-option :value="2" label="每 2 秒" />
                <el-option :value="3" label="每 3 秒" />
                <el-option :value="5" label="每 5 秒" />
              </el-select>
              <el-button size="small" :icon="Refresh" :loading="liveLoading" @click="loadLive">刷新</el-button>
              <el-input v-model="liveKeyword" size="small" clearable placeholder="标签 / 内容过滤" class="sel-kw" :prefix-icon="Search" />
              <el-checkbox v-model="liveOnlyTagged" size="small">仅带标签</el-checkbox>
              <div class="spacer" />
              <span v-if="live" class="muted">
                合订 {{ live.taggedCount }} · 滚动 {{ live.untaggedCount }} · 共 {{ live.total }}
                <span v-if="live.serverTime"> · 更新 {{ formatClock(live.serverTime) }}</span>
              </span>
            </div>

            <el-alert v-if="liveError" type="warning" :closable="false" show-icon :title="liveError" style="margin: 0 0 10px" />

            <el-table
              v-loading="liveLoading && liveFirstLoad"
              :data="filteredLive" size="small"
              max-height="calc(100vh - 392px)"
              :row-class-name="liveRowClass"
              class="live-table" @row-dblclick="(r: LiveDiagItem) => showDetail(r)">
              <el-table-column label="时间" width="130">
                <template #default="{ row }">{{ formatClock(row.time) }}</template>
              </el-table-column>
              <el-table-column label="标签" width="170">
                <template #default="{ row }">
                  <el-tag v-if="row.tagged" size="small" effect="dark">{{ row.tag }}</el-tag>
                  <span v-else class="muted">— 滚动</span>
                </template>
              </el-table-column>
              <el-table-column label="内容" show-overflow-tooltip>
                <template #default="{ row }"><span class="mono">{{ row.content }}</span></template>
              </el-table-column>
              <el-table-column label="操作" width="70" align="center">
                <template #default="{ row }">
                  <el-button link type="primary" :icon="View" @click="showDetail(row)" />
                </template>
              </el-table-column>
              <template #empty>
                <span class="muted">{{ liveError ? 'SimpleLite 未连接' : '暂无诊断消息' }}</span>
              </template>
            </el-table>
          </el-tab-pane>

          <!-- 日志文件 -->
          <el-tab-pane name="files">
            <template #label><el-icon><FolderOpened /></el-icon><span class="tab-lbl">日志文件</span></template>

            <div class="toolbar">
              <el-button size="small" :icon="Back" :disabled="browse?.parent == null" @click="enterDir(browse?.parent ?? '')">上一级</el-button>
              <el-breadcrumb separator="/" class="crumbs">
                <el-breadcrumb-item v-for="c in crumbs" :key="c.path">
                  <a class="crumb" @click="enterDir(c.path)">{{ c.label }}</a>
                </el-breadcrumb-item>
              </el-breadcrumb>
              <el-button size="small" :icon="Refresh" :loading="browseLoading" @click="loadBrowse">刷新</el-button>
              <el-input v-model="browseKeyword" size="small" clearable placeholder="按名称过滤" class="sel-kw" :prefix-icon="Search" />
              <div class="spacer" />
              <span v-if="browse" class="muted">{{ browse.dirCount }} 个文件夹 · {{ browse.fileCount }} 个文件</span>
            </div>

            <el-alert v-if="browse && !browse.exists" type="info" :closable="false" show-icon
              :title="browse.message || '日志目录尚未生成。'" style="margin: 0 0 10px" />

            <el-table
              v-loading="browseLoading" :data="browseRows" size="small"
              max-height="calc(100vh - 392px)"
              class="files-table" @row-dblclick="onRowActivate">
              <el-table-column label="名称" min-width="280">
                <template #default="{ row }">
                  <span class="name-cell" :class="{ 'is-dir': row.kind === 'dir' }">
                    <el-icon><component :is="row.kind === 'dir' ? Folder : Document" /></el-icon>
                    <span class="mono">{{ row.name }}</span>
                  </span>
                </template>
              </el-table-column>
              <el-table-column label="类型" width="160">
                <template #default="{ row }">
                  <span v-if="row.kind === 'dir'" class="muted">文件夹（{{ row.fileCount }} 文件）</span>
                  <el-tag v-else size="small" effect="plain" :type="row.isLog ? 'success' : 'info'">{{ row.ext || '文件' }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="大小" width="110" align="right">
                <template #default="{ row }">{{ row.kind === 'dir' ? '—' : formatBytes(row.bytes) }}</template>
              </el-table-column>
              <el-table-column label="修改时间" width="180">
                <template #default="{ row }">{{ formatTime(row.mtime) }}</template>
              </el-table-column>
              <el-table-column label="操作" width="240" align="center">
                <template #default="{ row }">
                  <template v-if="row.kind === 'dir'">
                    <el-button link type="primary" :icon="FolderOpened" @click="enterDir(row.rel)">进入</el-button>
                  </template>
                  <template v-else>
                    <el-button link type="primary" :icon="View" @click="openFile(row.rel)">打开</el-button>
                    <el-button v-if="row.isLog" link type="primary" :icon="TrendCharts" @click="analyzeFileFromBrowser(row.rel)">分析</el-button>
                    <el-button link type="primary" :icon="Download" @click="download(row.rel)">下载</el-button>
                  </template>
                </template>
              </el-table-column>
              <template #empty><span class="muted">该目录为空</span></template>
            </el-table>
          </el-tab-pane>

          <!-- 日志分析 -->
          <el-tab-pane name="analyze">
            <template #label><el-icon><TrendCharts /></el-icon><span class="tab-lbl">日志分析</span></template>

            <div v-if="!analyzeFile" class="analyze-empty">
              <el-empty description="从「日志文件」选择一个 .log 文件进行分析">
                <el-button type="primary" :icon="FolderOpened" @click="gotoFiles">去选择日志文件</el-button>
              </el-empty>
            </div>

            <template v-else>
              <div class="toolbar">
                <el-tag size="small" type="info" effect="plain" class="an-target">
                  <el-icon><Document /></el-icon><span class="mono">{{ analyzeFile }}</span>
                </el-tag>
                <el-button size="small" :icon="FolderOpened" @click="gotoFiles">换文件</el-button>
                <div class="spacer" />
                <span class="muted">粒度</span>
                <el-select v-model="anGran" size="small" class="sel-interval">
                  <el-option value="second" label="按秒" />
                  <el-option value="minute" label="按分" />
                  <el-option value="hour" label="按时" />
                </el-select>
                <el-select v-model="anTag" size="small" clearable placeholder="聚焦标签" class="sel-tag">
                  <el-option v-for="t in (analysis?.tags ?? [])" :key="t.tag || '__none__'" :value="t.tag" :label="`${tagLabel(t.tag)} (${t.count})`" />
                </el-select>
                <el-input v-model="anKeyword" size="small" clearable placeholder="关键字过滤" class="sel-kw" :prefix-icon="Search" @keyup.enter="loadAnalyze" />
                <el-button size="small" type="primary" :icon="Refresh" :loading="analyzeLoading" @click="loadAnalyze">分析</el-button>
              </div>

              <el-alert v-if="analysis?.truncated" type="warning" :closable="false" show-icon
                title="文件较大，仅分析了前部分内容，统计为近似值。" style="margin: 0 0 10px" />

              <div v-if="analysis" class="an-stats">
                <div class="ov-stat"><span class="k">总条数</span><span class="v">{{ analysis.total }}</span></div>
                <div class="ov-stat"><span class="k">标签数</span><span class="v">{{ analysis.tags.length }}</span></div>
                <div class="ov-stat"><span class="k">数值字段</span><span class="v">{{ analysis.fields.length }}</span></div>
                <div class="ov-stat"><span class="k">时间范围</span><span class="v">{{ anTimeRange }}</span></div>
              </div>

              <div class="an-grid" v-loading="analyzeLoading">
                <el-card shadow="never" class="an-card">
                  <div class="an-card-title">标签分布（Top 12）</div>
                  <div ref="tagChartRef" class="an-chart"></div>
                </el-card>
                <el-card shadow="never" class="an-card">
                  <div class="an-card-title">日志量随时间（Top 标签堆叠 + 总量）</div>
                  <div ref="volChartRef" class="an-chart"></div>
                </el-card>
              </div>

              <el-card shadow="never" class="an-card an-fields">
                <div class="an-card-title">
                  数值字段 <span class="muted">（识别 key=value / key:value，点「绘制」查看时序）</span>
                </div>
                <div class="an-fields-body">
                  <el-table :data="analysis?.fields ?? []" size="small" max-height="280" class="fields-table">
                    <el-table-column label="字段" min-width="110"><template #default="{ row }"><span class="mono">{{ row.name }}</span></template></el-table-column>
                    <el-table-column label="样本" width="76" align="right"><template #default="{ row }">{{ row.samples }}</template></el-table-column>
                    <el-table-column label="最小" width="86" align="right"><template #default="{ row }">{{ row.min }}</template></el-table-column>
                    <el-table-column label="最大" width="86" align="right"><template #default="{ row }">{{ row.max }}</template></el-table-column>
                    <el-table-column label="均值" width="96" align="right"><template #default="{ row }">{{ row.avg }}</template></el-table-column>
                    <el-table-column label="操作" width="92" align="center">
                      <template #default="{ row }">
                        <el-button link :type="anField === row.name ? 'success' : 'primary'" @click="pickField(row.name)">
                          {{ anField === row.name ? '已绘制' : '绘制' }}
                        </el-button>
                      </template>
                    </el-table-column>
                    <template #empty><span class="muted">未识别到数值字段</span></template>
                  </el-table>
                  <div class="an-field-chart-wrap">
                    <div v-show="anField" ref="fieldChartRef" class="an-chart tall"></div>
                    <div v-show="!anField" class="an-field-hint muted">选择左侧一个数值字段查看其时序曲线</div>
                  </div>
                </div>
              </el-card>
            </template>
          </el-tab-pane>
        </el-tabs>
      </el-card>

      <!-- 条目详情 -->
      <el-dialog v-model="detailVisible" title="日志条目详情" width="680px" append-to-body>
        <template v-if="detailItem">
          <el-descriptions :column="1" border size="small">
            <el-descriptions-item label="时间">{{ detailItem.time ? formatTime(detailItem.time) : '（无时间戳）' }}</el-descriptions-item>
            <el-descriptions-item label="标签">
              <el-tag v-if="detailItem.tag" size="small" effect="dark">{{ detailItem.tag }}</el-tag>
              <span v-else class="muted">（无，滚动记录）</span>
            </el-descriptions-item>
          </el-descriptions>
          <div class="detail-content">
            <div class="detail-bar">
              <span>内容</span>
              <el-button size="small" link type="primary" :icon="CopyDocument" @click="copy(detailItem.content)">复制</el-button>
            </div>
            <pre class="detail-pre">{{ detailItem.content }}</pre>
          </div>
        </template>
      </el-dialog>

      <!-- 打开文件抽屉 -->
      <el-drawer v-model="fileDrawer" :title="fileRel" size="64%" append-to-body>
        <div class="file-bar">
          <el-radio-group v-model="fileMode" size="small" @change="onFileModeChange">
            <el-radio-button label="structured">结构化</el-radio-button>
            <el-radio-button label="raw">原文</el-radio-button>
          </el-radio-group>
          <template v-if="fileMode === 'structured'">
            <el-input v-model="fileKeyword" size="small" clearable placeholder="内容 / 标签过滤" class="sel-kw" :prefix-icon="Search" />
            <el-checkbox v-model="fileOnlyTagged" size="small">仅带标签</el-checkbox>
          </template>
          <template v-else>
            <el-select v-model="rawTail" size="small" class="sel-tail" @change="reloadFile">
              <el-option :value="1000" label="尾部 1000 行" />
              <el-option :value="3000" label="尾部 3000 行" />
              <el-option :value="10000" label="尾部 1 万行" />
              <el-option :value="50000" label="尾部 5 万行" />
            </el-select>
          </template>
          <span v-if="fileMode === 'raw' && rawText" class="muted">{{ rawLineCount }} 行</span>
          <span v-else-if="fileMode === 'structured' && fileEntries" class="muted">{{ filteredFileEntries.length }} / {{ fileEntries.total }} 条</span>
          <div class="spacer" />
          <el-button v-if="fileRel.toLowerCase().endsWith('.log')" size="small" type="primary" plain :icon="TrendCharts" @click="analyzeFileFromBrowser(fileRel)">分析此文件</el-button>
          <el-button size="small" :icon="Download" @click="download(fileRel)">下载</el-button>
          <el-button size="small" :icon="Refresh" :loading="fileLoading" @click="reloadFile">刷新</el-button>
        </div>

        <el-alert v-if="fileMode === 'structured' && fileEntries?.truncated" type="warning" :closable="false" show-icon
          title="文件较大，仅解析了前部分条目。" style="margin: 0 0 10px" />

        <el-table
          v-if="fileMode === 'structured'"
          v-loading="fileLoading" :data="filteredFileEntries" size="small"
          max-height="calc(100vh - 220px)" class="entries-table"
          @row-dblclick="(r: LogEntry) => showDetail({ time: r.time, tag: r.tag, content: r.content })">
          <el-table-column label="时间" width="190">
            <template #default="{ row }">{{ row.time ? formatTime(row.time) : '—' }}</template>
          </el-table-column>
          <el-table-column label="标签" width="160">
            <template #default="{ row }">
              <el-tag v-if="row.tag" size="small" effect="dark">{{ row.tag }}</el-tag>
              <span v-else class="muted">—</span>
            </template>
          </el-table-column>
          <el-table-column label="内容" show-overflow-tooltip>
            <template #default="{ row }"><span class="mono">{{ row.content }}</span></template>
          </el-table-column>
          <el-table-column label="操作" width="70" align="center">
            <template #default="{ row }">
              <el-button link type="primary" :icon="View" @click="showDetail({ time: row.time, tag: row.tag, content: row.content })" />
            </template>
          </el-table-column>
        </el-table>

        <pre v-else v-loading="fileLoading" class="raw-pre">{{ rawText }}</pre>
      </el-drawer>
    </div>
  </PermissionGuard>
</template>

<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import {
  Refresh, Document, Search, View, Download, CopyDocument,
  Folder, FolderOpened, Back, DataLine, TrendCharts
} from '@element-plus/icons-vue'
// 按需引入 echarts：仅 bar/line 图 + grid/tooltip/legend/dataZoom 组件 + canvas 渲染器，
// 避免全量 echarts(~1MB) 打进 bundle。
import * as echarts from 'echarts/core'
import { BarChart, LineChart } from 'echarts/charts'
import { GridComponent, TooltipComponent, LegendComponent, DataZoomComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
import PermissionGuard from '@/components/PermissionGuard.vue'
import {
  logsApi,
  type LogOverview, type LiveDiagnosis, type LiveDiagItem,
  type BrowseResult, type LogEntry, type LogEntriesResult,
  type AnalyzeResult
} from '@/api/logs'

echarts.use([BarChart, LineChart, GridComponent, TooltipComponent, LegendComponent, DataZoomComponent, CanvasRenderer])

type Tab = 'live' | 'files' | 'analyze'
interface DetailItem { time: string | null; tag: string; content: string }
interface BrowseRow {
  kind: 'dir' | 'file'
  name: string
  rel: string
  mtime: string
  bytes: number
  fileCount: number
  isLog: boolean
  ext: string
}

const activeTab = ref<Tab>('live')

const overview = ref<LogOverview | null>(null)
const overviewLoading = ref(false)

// ── 实时诊断 ──
const live = ref<LiveDiagnosis | null>(null)
const liveLoading = ref(false)
const liveFirstLoad = ref(true)
const liveError = ref('')
const autoRefresh = ref(true)
const intervalSec = ref(2)
const liveKeyword = ref('')
const liveOnlyTagged = ref(false)
let timer: number | undefined

// ── 文件浏览 ──
const browse = ref<BrowseResult | null>(null)
const browseLoading = ref(false)
const browsePath = ref('')
const browseKeyword = ref('')

// ── 详情 ──
const detailVisible = ref(false)
const detailItem = ref<DetailItem | null>(null)

// ── 文件抽屉 ──
const fileDrawer = ref(false)
const fileRel = ref('')
const fileMode = ref<'structured' | 'raw'>('structured')
const fileLoading = ref(false)
const fileEntries = ref<LogEntriesResult | null>(null)
const rawText = ref('')
const rawTail = ref(3000)
const fileKeyword = ref('')
const fileOnlyTagged = ref(false)
const rawLineCount = computed(() => (rawText.value ? rawText.value.split('\n').length : 0))

function formatBytes(n: number): string {
  if (!n) return '0 B'
  if (n < 1024) return `${n} B`
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`
  return `${(n / 1024 / 1024).toFixed(2)} MB`
}
function formatTime(iso: string): string {
  const d = new Date(iso)
  return Number.isNaN(d.getTime()) ? iso : d.toLocaleString('zh-CN', { hour12: false })
}
function formatClock(iso: string): string {
  const d = new Date(iso)
  if (Number.isNaN(d.getTime())) return iso
  return d.toLocaleTimeString('zh-CN', { hour12: false }) + '.' + String(d.getMilliseconds()).padStart(3, '0')
}

// ── 实时诊断逻辑 ──
function tms(s: string): number {
  const d = new Date(s).getTime()
  return Number.isNaN(d) ? 0 : d
}
const filteredLive = computed<LiveDiagItem[]>(() => {
  let list = live.value?.items ?? []
  if (liveOnlyTagged.value) list = list.filter((i) => i.tagged)
  const kw = liveKeyword.value.trim().toLowerCase()
  if (kw) list = list.filter((i) => i.content.toLowerCase().includes(kw) || i.tag.toLowerCase().includes(kw))
  // 最新置顶：按时间倒序（合订条目用其最新时间）。
  return [...list].sort((a, b) => tms(b.time) - tms(a.time))
})
function liveRowClass({ row }: { row: LiveDiagItem }): string {
  return row.tagged ? 'tagged-row' : ''
}

async function loadLive() {
  liveLoading.value = true
  try {
    live.value = await logsApi.liveDiagnosis()
    liveError.value = ''
  } catch (e) {
    liveError.value = `实时诊断不可用：${(e as Error).message}`
  } finally {
    liveLoading.value = false
    liveFirstLoad.value = false
  }
}

function startTimer() {
  stopTimer()
  if (autoRefresh.value && activeTab.value === 'live') {
    timer = window.setInterval(loadLive, intervalSec.value * 1000)
  }
}
function stopTimer() {
  if (timer) { window.clearInterval(timer); timer = undefined }
}
watch([autoRefresh, intervalSec], startTimer)
watch(activeTab, (t) => {
  if (t === 'live') { void loadLive(); startTimer() } else { stopTimer() }
  if (t === 'analyze') nextTick(renderAnalyzeCharts)
})

// ── 文件浏览逻辑 ──
const crumbs = computed(() => {
  const segs = browsePath.value ? browsePath.value.split('/') : []
  const acc: Array<{ label: string; path: string }> = [{ label: 'log', path: '' }]
  let cur = ''
  for (const s of segs) { cur = cur ? `${cur}/${s}` : s; acc.push({ label: s, path: cur }) }
  return acc
})
const browseRows = computed<BrowseRow[]>(() => {
  const b = browse.value
  if (!b) return []
  const dirs: BrowseRow[] = b.dirs.map((d) => ({
    kind: 'dir', name: d.name, rel: d.rel, mtime: d.mtime, bytes: 0, fileCount: d.fileCount, isLog: false, ext: ''
  }))
  const files: BrowseRow[] = b.files.map((f) => ({
    kind: 'file', name: f.name, rel: f.rel, mtime: f.mtime, bytes: f.bytes, fileCount: 0, isLog: f.isLog,
    ext: f.name.includes('.') ? f.name.slice(f.name.lastIndexOf('.')) : ''
  }))
  let rows = [...dirs, ...files]
  const kw = browseKeyword.value.trim().toLowerCase()
  if (kw) rows = rows.filter((r) => r.name.toLowerCase().includes(kw))
  return rows
})

async function loadBrowse() {
  browseLoading.value = true
  try {
    browse.value = await logsApi.browse(browsePath.value || undefined)
    browsePath.value = browse.value.path
  } catch (e) {
    ElMessage.error(`浏览目录失败：${(e as Error).message}`)
  } finally {
    browseLoading.value = false
  }
}
function enterDir(path: string) {
  browsePath.value = path
  void loadBrowse()
}
function onRowActivate(row: BrowseRow) {
  if (row.kind === 'dir') enterDir(row.rel)
  else openFile(row.rel)
}

// ── 文件抽屉逻辑 ──
function openFile(rel: string) {
  fileRel.value = rel
  fileDrawer.value = true
  fileKeyword.value = ''
  fileOnlyTagged.value = false
  fileEntries.value = null
  rawText.value = ''
  fileMode.value = rel.toLowerCase().endsWith('.log') ? 'structured' : 'raw'
  void loadFileContent()
}
async function loadFileContent() {
  fileLoading.value = true
  try {
    if (fileMode.value === 'structured') {
      fileEntries.value = await logsApi.entries({ file: fileRel.value, order: 'desc', limit: 1000 })
    } else {
      rawText.value = await logsApi.raw(fileRel.value, rawTail.value)
    }
  } catch (e) {
    if (fileMode.value === 'raw') rawText.value = `读取失败：${(e as Error).message}`
    else ElMessage.error(`解析失败：${(e as Error).message}`)
  } finally {
    fileLoading.value = false
  }
}
function onFileModeChange() {
  if (fileMode.value === 'structured' && !fileEntries.value) void loadFileContent()
  else if (fileMode.value === 'raw' && !rawText.value) void loadFileContent()
}
function reloadFile() {
  if (fileMode.value === 'structured') fileEntries.value = null
  else rawText.value = ''
  void loadFileContent()
}
const filteredFileEntries = computed<LogEntry[]>(() => {
  let list = fileEntries.value?.entries ?? []
  if (fileOnlyTagged.value) list = list.filter((e) => e.tag)
  const kw = fileKeyword.value.trim().toLowerCase()
  if (kw) list = list.filter((e) => e.content.toLowerCase().includes(kw) || e.tag.toLowerCase().includes(kw))
  return list
})

// ── 日志分析器 ──
const analyzeFile = ref('')
const analyzeLoading = ref(false)
const analysis = ref<AnalyzeResult | null>(null)
const anGran = ref<'second' | 'minute' | 'hour'>('minute')
const anTag = ref('')
const anField = ref('')
const anKeyword = ref('')

const tagChartRef = ref<HTMLDivElement | null>(null)
const volChartRef = ref<HTMLDivElement | null>(null)
const fieldChartRef = ref<HTMLDivElement | null>(null)
type EChartsInstance = ReturnType<typeof echarts.init>
let tagInst: EChartsInstance | null = null
let volInst: EChartsInstance | null = null
let fieldInst: EChartsInstance | null = null

const CHART_PALETTE = ['#7c3aed', '#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#06b6d4', '#ec4899', '#84cc16']

function cssVar(name: string, fallback: string): string {
  if (typeof window === 'undefined') return fallback
  const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim()
  return v || fallback
}
function tagLabel(t: string): string { return t === '' ? '（滚动记录）' : t }

const anTimeRange = computed(() => {
  const r = analysis.value?.timeRange
  if (!r?.start || !r?.end) return '—'
  return `${formatTime(r.start)} ~ ${formatTime(r.end)}`
})

async function loadAnalyze() {
  if (!analyzeFile.value) return
  analyzeLoading.value = true
  try {
    analysis.value = await logsApi.analyze({
      file: analyzeFile.value,
      granularity: anGran.value,
      tag: anTag.value || undefined,
      field: anField.value || undefined,
      keyword: anKeyword.value.trim() || undefined
    })
  } catch (e) {
    ElMessage.error(`分析失败：${(e as Error).message}`)
  } finally {
    analyzeLoading.value = false
  }
}
function analyzeFileFromBrowser(rel: string) {
  fileDrawer.value = false
  analyzeFile.value = rel
  anTag.value = ''
  anField.value = ''
  anKeyword.value = ''
  activeTab.value = 'analyze'
  void loadAnalyze()
}
function pickField(name: string) {
  anField.value = anField.value === name ? '' : name
}
function gotoFiles() { activeTab.value = 'files' }

watch([anGran, anTag, anField], () => { if (analyzeFile.value) void loadAnalyze() })

// ── echarts 渲染 ──
function renderTagChart() {
  if (!tagChartRef.value) return
  const a = analysis.value
  tagInst?.dispose()
  tagInst = echarts.init(tagChartRef.value, undefined, { renderer: 'canvas' })
  const textMuted = cssVar('--mg-text-muted', '#909399')
  const grid = cssVar('--mg-divider', 'rgba(255,255,255,0.08)')
  const top = (a?.tags ?? []).slice(0, 12).slice().reverse()
  tagInst.setOption({
    backgroundColor: 'transparent',
    grid: { left: 8, right: 28, top: 10, bottom: 6, containLabel: true },
    tooltip: {
      trigger: 'axis', axisPointer: { type: 'shadow' },
      formatter: (params: unknown) => {
        const p = (params as Array<{ name: string; value: number }>)[0]
        return `${p.name}: <b>${p.value}</b> 条`
      }
    },
    xAxis: { type: 'value', axisLine: { show: false }, axisLabel: { color: textMuted, fontSize: 11 }, splitLine: { lineStyle: { color: grid, type: 'dashed' } } },
    yAxis: { type: 'category', data: top.map((t) => tagLabel(t.tag)), axisLine: { lineStyle: { color: grid } }, axisLabel: { color: textMuted, fontSize: 11 } },
    series: [{
      type: 'bar', barMaxWidth: 16,
      data: top.map((t, i) => ({ value: t.count, itemStyle: { color: CHART_PALETTE[(top.length - 1 - i) % CHART_PALETTE.length], borderRadius: [0, 3, 3, 0] } })),
      label: { show: true, position: 'right', color: textMuted, fontSize: 11 }
    }]
  })
}

function renderVolChart() {
  if (!volChartRef.value) return
  const a = analysis.value
  volInst?.dispose()
  volInst = echarts.init(volChartRef.value, undefined, { renderer: 'canvas' })
  const textMuted = cssVar('--mg-text-muted', '#909399')
  const grid = cssVar('--mg-divider', 'rgba(255,255,255,0.08)')
  const buckets = (a?.volume.buckets ?? []).map((t) => new Date(t).getTime())
  const topTags = a?.volume.topTags ?? []
  const series: Array<Record<string, unknown>> = topTags.map((tt, i) => ({
    name: tagLabel(tt.tag), type: 'bar', stack: 'vol', barMaxWidth: 18,
    data: buckets.map((b, j) => [b, tt.counts[j]]),
    itemStyle: { color: CHART_PALETTE[i % CHART_PALETTE.length] }
  }))
  series.push({
    name: '总量', type: 'line', smooth: true, showSymbol: false, z: 5,
    data: buckets.map((b, j) => [b, a?.volume.total[j] ?? 0]),
    lineStyle: { width: 2, color: cssVar('--mg-text-light', '#e5e7eb') }
  })
  volInst.setOption({
    backgroundColor: 'transparent',
    grid: { left: 36, right: 16, top: 30, bottom: 28, containLabel: true },
    tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
    legend: { top: 0, textStyle: { color: textMuted, fontSize: 11 }, itemHeight: 8, itemWidth: 12 },
    xAxis: { type: 'time', axisLine: { lineStyle: { color: grid } }, axisLabel: { color: textMuted, fontSize: 11 }, splitLine: { show: false } },
    yAxis: { type: 'value', axisLine: { show: false }, axisLabel: { color: textMuted, fontSize: 11 }, splitLine: { lineStyle: { color: grid, type: 'dashed' } } },
    series
  })
}

function renderFieldChart() {
  if (!fieldChartRef.value) return
  const a = analysis.value
  fieldInst?.dispose()
  fieldInst = echarts.init(fieldChartRef.value, undefined, { renderer: 'canvas' })
  const textMuted = cssVar('--mg-text-muted', '#909399')
  const grid = cssVar('--mg-divider', 'rgba(255,255,255,0.08)')
  const accent = cssVar('--mg-accent', '#7c3aed')
  const pts = a?.series?.points ?? []
  fieldInst.setOption({
    backgroundColor: 'transparent',
    grid: { left: 44, right: 16, top: 18, bottom: pts.length > 60 ? 40 : 28, containLabel: true },
    tooltip: { trigger: 'axis' },
    dataZoom: pts.length > 60 ? [{ type: 'inside' }, { type: 'slider', height: 16, bottom: 4 }] : [],
    xAxis: { type: 'time', axisLine: { lineStyle: { color: grid } }, axisLabel: { color: textMuted, fontSize: 11 }, splitLine: { show: false } },
    yAxis: { type: 'value', scale: true, axisLine: { show: false }, axisLabel: { color: textMuted, fontSize: 11 }, splitLine: { lineStyle: { color: grid, type: 'dashed' } } },
    series: [{
      name: a?.series?.field ?? '', type: 'line', smooth: true, showSymbol: pts.length < 80, symbolSize: 4,
      data: pts.map((p) => [new Date(p.t).getTime(), p.v]),
      lineStyle: { width: 2, color: accent }, itemStyle: { color: accent },
      areaStyle: { color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: accent + '44' }, { offset: 1, color: accent + '00' }] } }
    }]
  })
}

function renderAnalyzeCharts() {
  renderTagChart()
  renderVolChart()
  renderFieldChart()
}
watch(analysis, () => { if (activeTab.value === 'analyze') nextTick(renderAnalyzeCharts) })
function onChartResize() { tagInst?.resize(); volInst?.resize(); fieldInst?.resize() }

// ── 通用 ──
function showDetail(item: DetailItem | LiveDiagItem) {
  detailItem.value = { time: item.time, tag: item.tag, content: item.content }
  detailVisible.value = true
}
function download(rel: string) {
  const a = document.createElement('a')
  a.href = logsApi.downloadUrl(rel)
  a.download = rel.split('/').pop() ?? 'log.log'
  document.body.appendChild(a)
  a.click()
  a.remove()
}
async function copy(text: string) {
  try {
    await navigator.clipboard.writeText(text)
    ElMessage.success('已复制')
  } catch {
    ElMessage.warning('复制失败，请手动选择文本')
  }
}
async function loadOverview() {
  overviewLoading.value = true
  try {
    overview.value = await logsApi.overview()
  } catch (e) {
    ElMessage.error(`加载概览失败：${(e as Error).message}`)
  } finally {
    overviewLoading.value = false
  }
}
async function refreshAll() {
  await loadOverview()
  if (activeTab.value === 'live') await loadLive()
  else await loadBrowse()
}

onMounted(async () => {
  await loadOverview()
  await loadLive()
  startTimer()
  void loadBrowse()
  window.addEventListener('resize', onChartResize)
})
onUnmounted(() => {
  stopTimer()
  window.removeEventListener('resize', onChartResize)
  tagInst?.dispose(); tagInst = null
  volInst?.dispose(); volInst = null
  fieldInst?.dispose(); fieldInst = null
})
</script>

<style scoped>
.logview {
  display: flex;
  flex-direction: column;
  gap: 14px;
  flex: 1;
  min-height: 0;
}
.ov-card :deep(.el-card__body) { padding: 16px 18px; }
.ov-head { display: flex; align-items: center; justify-content: space-between; }
.ov-title { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 600; color: var(--mg-text-light); }
.ov-desc { color: var(--mg-text-muted); font-size: 12.5px; line-height: 1.6; margin: 10px 0 12px; }
.ov-desc code { font-family: var(--mg-font-mono, monospace); color: var(--mg-accent); }
.ov-stats { display: grid; grid-template-columns: 2.2fr 0.8fr 0.8fr 1.4fr; gap: 12px; }
.ov-stat { display: flex; flex-direction: column; gap: 3px; }
.ov-stat .k { font-size: 11px; color: var(--mg-text-muted); }
.ov-stat .v { font-size: 13px; color: var(--mg-text-light); font-variant-numeric: tabular-nums; }
.ov-stat .v.path { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: var(--mg-font-mono, monospace); }

.body-card { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.body-card :deep(.el-card__body) { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.log-tabs { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.log-tabs :deep(.el-tabs__content) { flex: 1; min-height: 0; }
.tab-lbl { margin-left: 5px; }

.toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 6px 0 12px; }
.toolbar .spacer { flex: 1; }
.sel-kw { width: 200px; }
.sel-interval { width: 120px; }
.muted { color: var(--mg-text-muted); font-size: 12px; }
.mono { font-family: var(--mg-font-mono, monospace); font-size: 12.5px; }

.crumbs { display: inline-flex; align-items: center; }
.crumb { cursor: pointer; color: var(--mg-accent); }
.crumb:hover { text-decoration: underline; }

.live-table, .files-table, .entries-table { width: 100%; }
.live-table :deep(.tagged-row) { background: rgba(var(--mg-primary-rgb), 0.06); }
.name-cell { display: inline-flex; align-items: center; gap: 6px; }
.name-cell.is-dir { color: var(--mg-accent); font-weight: 500; cursor: pointer; }

.detail-content { margin-top: 14px; }
.detail-bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; font-size: 13px; color: var(--mg-text-light); }
.detail-pre, .raw-pre {
  margin: 0; padding: 12px;
  font-family: var(--mg-font-mono, monospace); font-size: 12.5px; line-height: 1.55;
  background: var(--mg-veil-2, rgba(0,0,0,0.25)); color: var(--mg-text-light);
  border-radius: 8px; white-space: pre-wrap; word-break: break-all;
  max-height: 360px; overflow: auto;
}
.raw-pre { max-height: calc(100vh - 200px); white-space: pre; word-break: normal; }
.file-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.file-bar .spacer { flex: 1; }
.sel-tail { width: 140px; }

/* ── 日志分析 ── */
.sel-tag { width: 200px; }
.analyze-empty { display: flex; align-items: center; justify-content: center; min-height: 320px; }
.an-target { max-width: 360px; display: inline-flex; align-items: center; gap: 4px; overflow: hidden; }
.an-target .mono { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.an-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 0 0 12px; }
.an-grid { display: grid; grid-template-columns: 1fr 1.4fr; gap: 12px; margin-bottom: 12px; }
.an-card :deep(.el-card__body) { padding: 12px 14px; }
.an-card-title { font-size: 13px; font-weight: 600; color: var(--mg-text-light); margin-bottom: 8px; }
.an-card-title .muted { font-weight: 400; }
.an-chart { width: 100%; height: 260px; }
.an-chart.tall { height: 300px; }
.an-fields-body { display: grid; grid-template-columns: minmax(420px, 1fr) 1.2fr; gap: 14px; align-items: stretch; }
.an-field-chart-wrap { position: relative; min-height: 300px; border-left: 1px solid var(--mg-divider, rgba(255,255,255,0.08)); padding-left: 14px; }
.an-field-hint { display: flex; align-items: center; justify-content: center; height: 300px; }
@media (max-width: 1200px) {
  .an-grid { grid-template-columns: 1fr; }
  .an-fields-body { grid-template-columns: 1fr; }
  .an-field-chart-wrap { border-left: none; border-top: 1px solid var(--mg-divider, rgba(255,255,255,0.08)); padding-left: 0; padding-top: 12px; }
}
</style>
