<template>
  <div class="trend-charts">
    <div class="trend-toolbar">
      <div class="toolbar-left">
        <span class="toolbar-title">系统性能趋势</span>
        <span v-if="lastUpdatedAt" class="ctms-updated-at">最近成功更新：{{ formatLastUpdated(lastUpdatedAt) }}</span>
      </div>
      <div class="toolbar-actions">
        <TimeRangeSelector v-model="timeRangePreset" @change="loadData" />
        <el-button size="small" :loading="loading" @click="loadData">刷新</el-button>
      </div>
    </div>

    <el-alert v-if="loadError" :title="loadError" type="warning" show-icon :closable="false" class="load-alert" />

    <div v-loading="loading" class="charts-grid">
      <div class="chart-card" :class="{ 'is-empty': !hasEventSamples }">
        <div class="chart-header">
          <div class="chart-icon blue">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
          </div>
          <div class="chart-title-group">
            <h4>监测事件趋势</h4>
            <span>允许与拒绝</span>
          </div>
          <strong class="chart-latest">{{ latestEventLabel }}</strong>
        </div>
        <v-chart v-if="hasEventSamples" :option="checksChartOption" autoresize class="chart" />
        <div v-else class="trend-empty-state">所选时段暂无监测事件</div>
      </div>
      <div class="chart-card" :class="{ 'is-empty': !hasResponseSamples }">
        <div class="chart-header">
          <div class="chart-icon cyan">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
          </div>
          <div class="chart-title-group">
            <h4>响应时间趋势</h4>
            <span>平均值与最大值</span>
          </div>
          <strong class="chart-latest">{{ latestResponseLabel }}</strong>
        </div>
        <v-chart v-if="hasResponseSamples" :option="responseTimeOption" autoresize class="chart" />
        <div v-else class="trend-empty-state">暂无有效响应时间样本</div>
      </div>
      <div class="chart-card" :class="{ 'is-empty': !hasCacheSamples }">
        <div class="chart-header">
          <div class="chart-icon green">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
          </div>
          <div class="chart-title-group">
            <h4>缓存命中率</h4>
            <span>命中 / 总访问</span>
          </div>
          <strong class="chart-latest">{{ latestCacheLabel }}</strong>
        </div>
        <v-chart v-if="hasCacheSamples" :option="cacheHitOption" autoresize class="chart" />
        <div v-else class="trend-empty-state">暂无缓存访问样本</div>
      </div>
      <div class="chart-card" :class="{ 'is-empty': !hasDenySamples }">
        <div class="chart-header">
          <div class="chart-icon danger">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>
          </div>
          <div class="chart-title-group">
            <h4>异常拒绝率趋势</h4>
            <span>拒绝事件占比</span>
          </div>
          <strong class="chart-latest">{{ latestDenyLabel }}</strong>
        </div>
        <v-chart v-if="hasDenySamples" :option="denyRateOption" autoresize class="chart" />
        <div v-else class="trend-empty-state">暂无权限检查样本</div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import VChart from "vue-echarts";
import { use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers";
import { LineChart, BarChart } from "echarts/charts";
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  LegendComponent,
} from "echarts/components";
import { fetchPermissionTrends } from "@/api/projectPermissions";
import type { TrendDataPoint } from "@/types/api";
import TimeRangeSelector from "./TimeRangeSelector.vue";

use([CanvasRenderer, LineChart, BarChart, TitleComponent, TooltipComponent, GridComponent, LegendComponent]);

type TimeRangePreset = "all" | "24h" | "7d" | "30d";
const timeRangePreset = ref<TimeRangePreset>("24h");
const timeRangeOptions = [
  { value: "all", label: "全部" },
  { value: "24h", label: "24小时" },
  { value: "7d", label: "7天" },
  { value: "30d", label: "30天" },
] as const;
const loading = ref(false);
const dataPoints = ref<TrendDataPoint[]>([]);
const loadError = ref("");
const lastUpdatedAt = ref<Date | null>(null);

const formatLastUpdated = (value: Date) => {
  return `${value.getFullYear()}/${value.getMonth() + 1}/${value.getDate()} ${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}:${String(value.getSeconds()).padStart(2, "0")}`;
};

const formatTime = (iso: string) => {
  const d = new Date(iso);
  if (timeRangePreset.value === "24h") return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, "0")}:00`;
  return `${d.getMonth() + 1}/${d.getDate()}`;
};

const xLabels = computed(() => dataPoints.value.map((p: TrendDataPoint) => formatTime(p.bucket_time)));

const latestPoint = computed(() => dataPoints.value.at(-1) || null);
const hasEventSamples = computed(() => dataPoints.value.some((point) => point.total_checks > 0));
const hasResponseSamples = computed(() =>
  dataPoints.value.some((point) => point.avg_elapsed_ms > 0 || point.max_elapsed_ms > 0)
);
const hasCacheSamples = computed(() =>
  dataPoints.value.some((point) => point.cache_hits + point.cache_misses > 0)
);
const hasDenySamples = computed(() => dataPoints.value.some((point) => point.total_checks > 0));

const latestEventLabel = computed(() =>
  latestPoint.value && latestPoint.value.total_checks > 0
    ? `${latestPoint.value.total_checks} 次`
    : "暂无样本"
);
const latestResponseLabel = computed(() =>
  latestPoint.value && (latestPoint.value.avg_elapsed_ms > 0 || latestPoint.value.max_elapsed_ms > 0)
    ? `${latestPoint.value.avg_elapsed_ms.toFixed(1)} ms`
    : "暂无样本"
);
const latestCacheLabel = computed(() =>
  latestPoint.value && latestPoint.value.cache_hits + latestPoint.value.cache_misses > 0
    ? `${latestPoint.value.cache_hit_rate.toFixed(1)}%`
    : "暂无样本"
);
const latestDenyLabel = computed(() => {
  if (!latestPoint.value || latestPoint.value.total_checks <= 0) return "暂无样本";
  return `${((latestPoint.value.denied_checks / latestPoint.value.total_checks) * 100).toFixed(1)}%`;
});

const baseGridOption = {
  grid: { left: 50, right: 20, top: 20, bottom: 25 },
  tooltip: { trigger: "axis" as const },
};

const checksChartOption = computed(() => ({
  grid: { left: 50, right: 20, top: 20, bottom: 50 },
  tooltip: { trigger: "axis" as const },
  legend: { data: ["允许", "拒绝"], bottom: 0, itemGap: 20 },
  xAxis: { type: "category", data: xLabels.value, axisLine: { lineStyle: { color: "#e2e8f0" } }, axisLabel: { color: "#64748b" } },
  yAxis: { type: "value", minInterval: 1, splitLine: { lineStyle: { color: "#f1f5f9" } }, axisLabel: { color: "#64748b" } },
  series: [
    {
      name: "允许",
      type: "line",
      smooth: true,
      symbol: "circle",
      symbolSize: 6,
      data: dataPoints.value.map((p: TrendDataPoint) => p.allowed_checks),
      itemStyle: { color: "#10b981" },
      lineStyle: { width: 2.5 },
      areaStyle: { color: { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: "rgba(16,185,129,0.2)" }, { offset: 1, color: "rgba(16,185,129,0)" }] } },
    },
    {
      name: "拒绝",
      type: "line",
      smooth: true,
      symbol: "circle",
      symbolSize: 6,
      data: dataPoints.value.map((p: TrendDataPoint) => p.denied_checks),
      itemStyle: { color: "#ef4444" },
      lineStyle: { width: 2.5 },
      areaStyle: { color: { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: "rgba(239,68,68,0.15)" }, { offset: 1, color: "rgba(239,68,68,0)" }] } },
    },
  ],
}));

const responseTimeOption = computed(() => ({
  grid: { left: 50, right: 20, top: 20, bottom: 50 },
  tooltip: { trigger: "axis" as const },
  legend: { data: ["平均响应时间", "最大响应时间"], bottom: 0, itemGap: 20 },
  xAxis: { type: "category", data: xLabels.value, axisLine: { lineStyle: { color: "#e2e8f0" } }, axisLabel: { color: "#64748b" } },
  yAxis: { type: "value", name: "ms", splitLine: { lineStyle: { color: "#f1f5f9" } }, axisLabel: { color: "#64748b" } },
  series: [
    {
      name: "平均响应时间",
      type: "line",
      smooth: true,
      symbol: "circle",
      symbolSize: 6,
      data: dataPoints.value.map((p: TrendDataPoint) => p.avg_elapsed_ms),
      itemStyle: { color: "#3b82f6" },
      lineStyle: { width: 2.5 },
      areaStyle: { color: { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: "rgba(59,130,246,0.15)" }, { offset: 1, color: "rgba(59,130,246,0)" }] } },
    },
    {
      name: "最大响应时间",
      type: "line",
      smooth: true,
      symbol: "diamond",
      symbolSize: 6,
      data: dataPoints.value.map((p: TrendDataPoint) => p.max_elapsed_ms),
      itemStyle: { color: "#f59e0b" },
      lineStyle: { width: 2, type: "dashed" },
    },
  ],
}));

const cacheHitOption = computed(() => ({
  grid: { left: 50, right: 20, top: 20, bottom: 25 },
  tooltip: { trigger: "axis" as const },
  xAxis: { type: "category", data: xLabels.value, axisLine: { lineStyle: { color: "#e2e8f0" } }, axisLabel: { color: "#64748b" } },
  yAxis: { type: "value", max: 100, name: "%", splitLine: { lineStyle: { color: "#f1f5f9" } }, axisLabel: { color: "#64748b" } },
  series: [
    {
      name: "缓存命中率",
      type: "bar",
      barMaxWidth: 24,
      itemStyle: {
        borderRadius: [4, 4, 0, 0],
        color: (params: any) => {
          const val = params.value;
          if (val >= 80) return { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: "#34d399" }, { offset: 1, color: "#10b981" }] };
          if (val >= 50) return { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: "#fbbf24" }, { offset: 1, color: "#f59e0b" }] };
          return { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: "#f87171" }, { offset: 1, color: "#ef4444" }] };
        },
      },
      data: dataPoints.value.map((p: TrendDataPoint) => p.cache_hit_rate),
    },
  ],
}));

const denyRateOption = computed(() => ({
  grid: { left: 50, right: 20, top: 20, bottom: 25 },
  tooltip: { trigger: "axis" as const },
  xAxis: { type: "category", data: xLabels.value, axisLine: { lineStyle: { color: "#e2e8f0" } }, axisLabel: { color: "#64748b" } },
  yAxis: { type: "value", max: 100, name: "%", splitLine: { lineStyle: { color: "#f1f5f9" } }, axisLabel: { color: "#64748b" } },
  series: [
    {
      name: "异常拒绝率",
      type: "line",
      smooth: true,
      symbol: "circle",
      symbolSize: 6,
      data: dataPoints.value.map((p: TrendDataPoint) =>
        p.total_checks > 0 ? Math.round((p.denied_checks / p.total_checks) * 1000) / 10 : null
      ),
      itemStyle: { color: "#ef4444" },
      lineStyle: { width: 2.5 },
      areaStyle: { color: { type: "linear", x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: "rgba(239,68,68,0.2)" }, { offset: 1, color: "rgba(239,68,68,0)" }] } },
    },
  ],
}));

const loadData = async () => {
  loading.value = true;
  try {
    const apiPeriod = timeRangePreset.value === "all" ? "30d" : timeRangePreset.value;
    const res = await fetchPermissionTrends(apiPeriod);
    dataPoints.value = [...res.data.data_points].sort(
      (left, right) => new Date(left.bucket_time).getTime() - new Date(right.bucket_time).getTime(),
    );
    loadError.value = "";
    lastUpdatedAt.value = new Date();
  } catch {
    loadError.value = "趋势数据加载失败，图表中可能是上次成功获取的数据";
  } finally {
    loading.value = false;
  }
};

onMounted(loadData);

defineExpose({ refresh: loadData });
</script>

<style scoped>
.trend-charts {
  --trend-ink: #1a2332;
  --trend-muted: #64748b;
  --trend-border: #e2e8f0;
  --trend-radius: 14px;
  --trend-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 6px 18px rgba(15, 23, 42, 0.025);
  width: 100%;
  margin: 0;
  padding: 0 12px 12px;
  box-sizing: border-box;
}

.trend-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 48px;
  padding: 8px 14px;
  margin-bottom: 8px;
  background: #fff;
  border: 1px solid var(--trend-border);
  border-radius: var(--trend-radius);
  box-shadow: var(--trend-shadow);
}

.toolbar-left {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.toolbar-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--trend-ink);
}



.toolbar-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.toolbar-updated-at {
  color: var(--trend-muted);
  font-size: 12px;
  white-space: nowrap;
}

.charts-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

.load-alert {
  margin-bottom: 8px;
}

.chart-card {
  background: #fff;
  border: 1px solid var(--trend-border);
  border-radius: var(--trend-radius);
  min-height: 276px;
  padding: 12px 14px;
  box-shadow: var(--trend-shadow);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.chart-card:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.07);
}

.chart-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}

.chart-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  flex-shrink: 0;
}

.chart-icon svg {
  width: 14px;
  height: 14px;
}

.chart-icon.blue { background: #eff6ff; color: #3b82f6; }
.chart-icon.cyan { background: #ecfeff; color: #06b6d4; }
.chart-icon.green { background: #ecfdf5; color: #10b981; }
.chart-icon.danger { background: #fef2f2; color: #ef4444; }

.chart-card h4 {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--trend-ink);
}

.chart-title-group {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.chart-title-group span {
  color: var(--trend-muted);
  font-size: 11px;
}

.chart-latest {
  padding: 4px 7px;
  border-radius: 8px;
  background: #f8fafc;
  color: var(--trend-ink);
  font-size: 12px;
  font-weight: 650;
  white-space: nowrap;
}

.chart {
  height: 212px;
  width: 100%;
}

.trend-empty-state {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 212px;
  border: 1px dashed #dbe3ef;
  border-radius: 10px;
  background: #f8fafc;
  color: #94a3b8;
  font-size: 13px;
}

@media (max-width: 900px) {
  .charts-grid {
    grid-template-columns: 1fr;
  }

  .trend-toolbar {
    flex-wrap: wrap;
    gap: 8px;
  }

  .toolbar-actions {
    flex: 1 1 auto;
    flex-wrap: wrap;
  }

  .chart-card {
    min-height: 260px;
  }
}

@media (max-width: 600px) {
  .trend-charts {
    padding: 0 8px 10px;
  }

  .trend-toolbar,
  .toolbar-left {
    align-items: flex-start;
    flex-direction: column;
  }

  .toolbar-actions {
    width: 100%;
    justify-content: flex-start;
  }
}
</style>
