<template>
  <div class="rule-config-page">
    <!-- LLM 状态面板 (MVP-3 / D34) -->
    <section class="llm-panel" v-if="llmStatus">
      <div class="panel-header">
        <div class="panel-title">
          <el-icon :size="18"><Cpu /></el-icon>
          <span>LLM 二次判断</span>
          <el-tag
            :type="llmStatus.enabled ? 'success' : 'info'"
            effect="dark"
            round
            size="small"
          >
            {{ llmStatus.enabled ? '已启用' : '未启用' }}
          </el-tag>
          <el-tag
            v-if="llmCircuitState"
            :type="circuitTagType"
            effect="plain"
            round
            size="small"
          >
            熔断: {{ circuitLabel }}
          </el-tag>
        </div>
        <div class="panel-actions">
          <el-button size="small" @click="loadLLMStatus">
            <el-icon><Refresh /></el-icon>
            刷新
          </el-button>
          <el-button
            v-if="llmTrackerEnabled"
            size="small"
            type="warning"
            plain
            @click="handleLLMDisable"
          >
            <el-icon><VideoPause /></el-icon>
            紧急停用
          </el-button>
          <el-button
            v-else
            size="small"
            type="success"
            plain
            @click="handleLLMEnable"
          >
            <el-icon><VideoPlay /></el-icon>
            重新启用
          </el-button>
          <el-popconfirm title="确认重置成本统计与熔断？" @confirm="handleLLMReset">
            <template #reference>
              <el-button size="small" type="danger" plain>
                <el-icon><Refresh /></el-icon>
                重置统计
              </el-button>
            </template>
          </el-popconfirm>
        </div>
      </div>

      <div class="llm-grid">
        <div class="llm-card">
          <div class="llm-label">Provider</div>
          <div class="llm-value">{{ llmStatus.provider }}</div>
          <div class="llm-sub">{{ llmStatus.model || '-' }}</div>
        </div>
        <div class="llm-card">
          <div class="llm-label">融合策略</div>
          <div class="llm-value">{{ fusionLabel }}</div>
          <div class="llm-sub">
            YOLO {{ formatRatio(llmStatus.fusion?.yolo_weight) }} /
            LLM {{ formatRatio(llmStatus.fusion?.llm_weight) }}
          </div>
        </div>
        <div class="llm-card">
          <div class="llm-label">触发阈值</div>
          <div class="llm-value">
            {{ llmStatus.triggers?.min_consecutive_hits }} 连续帧
          </div>
          <div class="llm-sub">
            avg≥{{ formatRatio(llmStatus.triggers?.min_avg_confidence) }} ·
            冷却{{ llmStatus.triggers?.cooldown_seconds }}s
          </div>
        </div>
        <div class="llm-card">
          <div class="llm-label">今日调用</div>
          <div class="llm-value">{{ todayCost?.call_count ?? 0 }} 次</div>
          <div class="llm-sub">
            成功 {{ todayCost?.success_count ?? 0 }} /
            失败 {{ todayCost?.failure_count ?? 0 }}
          </div>
        </div>
        <div class="llm-card">
          <div class="llm-label">今日费用</div>
          <div class="llm-value">${{ formatCost(todayCost?.cost_usd) }}</div>
          <div class="llm-sub">
            预算
            {{ todayCost?.budget_usd ? `$${formatCost(todayCost.budget_usd)}` : '∞' }}
            <span v-if="todayCost?.budget_usd > 0" class="budget-bar-wrap">
              <span
                class="budget-bar"
                :class="{ 'is-danger': (todayCost?.budget_used_ratio ?? 0) >= 0.8 }"
                :style="{ width: `${Math.min(100, (todayCost?.budget_used_ratio ?? 0) * 100)}%` }"
              ></span>
            </span>
          </div>
        </div>
        <div class="llm-card">
          <div class="llm-label">最近错误率</div>
          <div class="llm-value">
            {{ formatRatio(llmStatus.cost?.recent_error_rate) }}
          </div>
          <div class="llm-sub">
            阈值 {{ formatRatio(llmStatus.cost?.error_rate_threshold) }}
          </div>
        </div>
      </div>

      <div v-if="llmStatus.cost?.block_reason" class="llm-warning">
        <el-icon><WarningFilled /></el-icon>
        当前不会调用 LLM，原因：{{ llmStatus.cost.block_reason }}
      </div>
    </section>

    <!-- 工具栏 -->
    <div class="toolbar">
      <div class="toolbar-left">
        <el-input
          v-model="filter.keyword"
          placeholder="按规则名/描述搜索"
          size="small"
          clearable
          class="filter-control"
        >
          <template #prefix>
            <el-icon><Search /></el-icon>
          </template>
        </el-input>
        <el-select
          v-model="filter.event_type"
          placeholder="事件类型"
          clearable
          size="small"
          class="filter-control"
        >
          <el-option
            v-for="opt in eventTypeOptions"
            :key="opt.value"
            :label="opt.label"
            :value="opt.value"
          />
        </el-select>
      </div>
      <div class="toolbar-right">
        <el-button size="small" @click="loadRules">
          <el-icon><Refresh /></el-icon>
          刷新
        </el-button>
        <el-button size="small" type="warning" plain @click="handleReload">
          <el-icon><MagicStick /></el-icon>
          热重载
        </el-button>
        <el-button size="small" type="primary" @click="openCreateDialog">
          <el-icon><Plus /></el-icon>
          新增规则
        </el-button>
      </div>
    </div>

    <!-- 规则列表 -->
    <div class="rule-list">
      <el-empty
        v-if="filteredRules.length === 0"
        description="暂无规则"
        class="empty-state"
      />

      <div
        v-for="rule in filteredRules"
        :key="rule.name"
        class="rule-card"
        :class="{ 'is-disabled': !rule.enabled }"
      >
        <div class="rule-bar" :class="`severity-${rule.severity || 'info'}`"></div>
        <div class="rule-body">
          <div class="rule-head">
            <div class="rule-title">
              <el-tag
                :type="severityTagType(rule.severity)"
                size="small"
                effect="dark"
                round
              >
                {{ severityLabel(rule.severity) }}
              </el-tag>
              <span class="rule-name">{{ rule.name }}</span>
              <el-tag size="small" effect="plain">
                {{ eventTypeLabel(rule.event_type) }}
              </el-tag>
              <el-switch
                :model-value="rule.enabled"
                size="small"
                @change="(v) => handleToggle(rule, v)"
              />
            </div>
            <div class="rule-actions">
              <el-button text size="small" @click="openEditDialog(rule)">
                <el-icon><Edit /></el-icon>
                编辑
              </el-button>
              <el-popconfirm
                title="确认删除该规则？(仅 custom.yaml 中的规则可删除)"
                @confirm="handleDelete(rule)"
              >
                <template #reference>
                  <el-button text type="danger" size="small">
                    <el-icon><Delete /></el-icon>
                    删除
                  </el-button>
                </template>
              </el-popconfirm>
            </div>
          </div>

          <div v-if="rule.description" class="rule-desc">{{ rule.description }}</div>

          <div class="rule-meta">
            <div class="meta-item">
              <el-icon><DataLine /></el-icon>
              <span>min_conf ≥ {{ formatRatio(rule.min_confidence) }}</span>
            </div>
            <div class="meta-item">
              <el-icon><Crop /></el-icon>
              <span>min_area ≥ {{ rule.min_bbox_area || 0 }}</span>
            </div>
            <div v-if="rule.allowed_sources?.length" class="meta-item">
              <el-icon><VideoCamera /></el-icon>
              <span>限定: {{ rule.allowed_sources.join(', ') }}</span>
            </div>
            <div v-if="rule.required_labels?.length" class="meta-item">
              <el-icon><Collection /></el-icon>
              <span>标签: {{ rule.required_labels.join(', ') }}</span>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- 编辑/新增规则弹窗 -->
    <el-dialog
      v-model="dialogVisible"
      :title="editingName ? '编辑规则' : '新增规则'"
      width="560px"
      :close-on-click-modal="false"
      class="rule-dialog"
    >
      <el-form
        ref="formRef"
        :model="formState"
        :rules="formRules"
        label-position="top"
      >
        <el-form-item label="规则名称" prop="name">
          <el-input
            v-model="formState.name"
            placeholder="例如 fire_high_priority"
            :disabled="!!editingName"
          />
        </el-form-item>
        <el-form-item label="事件类型" prop="event_type">
          <el-select v-model="formState.event_type" placeholder="选择事件类型" style="width: 100%">
            <el-option
              v-for="opt in eventTypeOptions"
              :key="opt.value"
              :label="opt.label"
              :value="opt.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="严重级别">
          <el-select v-model="formState.severity" placeholder="使用默认" clearable style="width: 100%">
            <el-option label="信息" value="info" />
            <el-option label="低危" value="low" />
            <el-option label="中等" value="medium" />
            <el-option label="高危" value="high" />
            <el-option label="严重" value="critical" />
          </el-select>
        </el-form-item>
        <el-form-item label="最低置信度">
          <el-slider
            v-model="formState.min_confidence"
            :min="0"
            :max="1"
            :step="0.05"
            :format-tooltip="(v) => Number(v).toFixed(2)"
          />
        </el-form-item>
        <el-form-item label="最小边界框面积">
          <el-input-number
            v-model="formState.min_bbox_area"
            :min="0"
            :step="100"
            controls-position="right"
          />
          <span class="form-hint">单位：像素²，0 表示不限</span>
        </el-form-item>
        <el-form-item label="限定摄像头 ID（可选）">
          <el-select
            v-model="formState.allowed_sources"
            multiple
            allow-create
            filterable
            default-first-option
            placeholder="留空表示所有摄像头"
            style="width: 100%"
          >
            <el-option
              v-for="opt in cameraOptions"
              :key="opt"
              :label="opt"
              :value="opt"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="必备标签（可选）">
          <el-select
            v-model="formState.required_labels"
            multiple
            allow-create
            filterable
            default-first-option
            placeholder="检测项 class_name 或 label 必须包含其中之一"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="描述">
          <el-input
            v-model="formState.description"
            type="textarea"
            :rows="2"
            placeholder="说明该规则的业务用途"
          />
        </el-form-item>
        <el-form-item label="启用">
          <el-switch v-model="formState.enabled" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" :loading="submitting" @click="handleSubmit">
          {{ editingName ? '保存' : '创建' }}
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {
  Search,
  Refresh,
  Plus,
  Edit,
  Delete,
  Cpu,
  DataLine,
  Crop,
  Collection,
  VideoCamera,
  VideoPlay,
  VideoPause,
  WarningFilled,
  MagicStick
} from '@element-plus/icons-vue'
import { ruleApi, llmApi, cameraApi } from '@/api/detection'

const eventTypeOptions = [
  { label: '火灾', value: 'fire' },
  { label: '烟雾', value: 'smoke' },
  { label: '抽烟', value: 'smoking' },
  { label: '打架', value: 'fight' },
  { label: '徘徊', value: 'loitering' },
  { label: '滞留', value: 'stationary' },
  { label: '入侵', value: 'intrusion' },
  { label: '违章停车', value: 'illegal_parking' },
  { label: '车辆', value: 'vehicle' },
  { label: '人员', value: 'person' }
]

const rules = ref([])
const llmStatus = ref(null)
const cameraOptions = ref([])
const filter = reactive({ keyword: '', event_type: '' })
const dialogVisible = ref(false)
const submitting = ref(false)
const editingName = ref('')
const formRef = ref(null)
const formState = reactive(makeEmptyForm())
let pollTimer = null

const formRules = {
  name: [
    { required: true, message: '请输入规则名称', trigger: 'blur' },
    { pattern: /^[A-Za-z0-9_\-]+$/, message: '仅允许字母、数字、下划线和短横线', trigger: 'blur' }
  ],
  event_type: [{ required: true, message: '请选择事件类型', trigger: 'change' }]
}

function makeEmptyForm() {
  return {
    name: '',
    event_type: 'fire',
    enabled: true,
    min_confidence: 0.5,
    severity: '',
    allowed_sources: [],
    required_labels: [],
    min_bbox_area: 0,
    description: ''
  }
}

const filteredRules = computed(() => {
  return rules.value.filter((r) => {
    if (filter.event_type && r.event_type !== filter.event_type) return false
    if (filter.keyword) {
      const kw = filter.keyword.toLowerCase()
      const text = `${r.name} ${r.description || ''}`.toLowerCase()
      if (!text.includes(kw)) return false
    }
    return true
  })
})

const todayCost = computed(() => llmStatus.value?.cost?.today)
const llmCircuitState = computed(() => llmStatus.value?.cost?.circuit_state)
const llmTrackerEnabled = computed(() => llmStatus.value?.cost?.enabled !== false)

const circuitLabel = computed(() => {
  const map = { closed: '正常', open: '熔断', half_open: '半开试探' }
  return map[llmCircuitState.value] || llmCircuitState.value || '-'
})

const circuitTagType = computed(() => {
  if (llmCircuitState.value === 'open') return 'danger'
  if (llmCircuitState.value === 'half_open') return 'warning'
  return 'success'
})

const fusionLabel = computed(() => {
  const map = {
    weighted: '加权融合',
    conservative: '保守策略',
    llm_priority: 'LLM 优先'
  }
  return map[llmStatus.value?.fusion?.strategy] || llmStatus.value?.fusion?.strategy || '-'
})

function severityTagType(severity) {
  const map = {
    critical: 'danger',
    high: 'warning',
    medium: 'primary',
    low: 'info',
    info: 'success'
  }
  return map[severity] || 'info'
}

function severityLabel(severity) {
  const map = {
    critical: '严重',
    high: '高危',
    medium: '中等',
    low: '低危',
    info: '信息'
  }
  return map[severity] || severity || '默认'
}

function eventTypeLabel(type) {
  const opt = eventTypeOptions.find((o) => o.value === type)
  return opt ? opt.label : type
}

function formatRatio(v) {
  if (v === undefined || v === null) return '-'
  return Number(v).toFixed(2)
}

function formatCost(v) {
  if (v === undefined || v === null) return '0.0000'
  return Number(v).toFixed(4)
}

async function loadRules() {
  try {
    const { data } = await ruleApi.list()
    rules.value = data?.rules || []
  } catch (err) {
    ElMessage.error(`加载规则失败: ${err.message || err}`)
  }
}

async function loadLLMStatus() {
  try {
    const { data } = await llmApi.status()
    llmStatus.value = data
  } catch (err) {
    // LLM 未启用时 cost 接口可能 503，状态接口仍可用 - 失败时静默
    console.warn('加载 LLM 状态失败:', err.message)
  }
}

async function loadCameras() {
  try {
    const { data } = await cameraApi.list()
    cameraOptions.value = (data?.streams || []).map((s) => s.stream_id)
  } catch (err) {
    // 摄像头列表加载失败不阻塞规则页
    cameraOptions.value = []
  }
}

function openCreateDialog() {
  editingName.value = ''
  Object.assign(formState, makeEmptyForm())
  dialogVisible.value = true
}

function openEditDialog(rule) {
  editingName.value = rule.name
  Object.assign(formState, {
    name: rule.name,
    event_type: rule.event_type,
    enabled: rule.enabled,
    min_confidence: Number(rule.min_confidence ?? 0),
    severity: rule.severity || '',
    allowed_sources: rule.allowed_sources || [],
    required_labels: rule.required_labels || [],
    min_bbox_area: Number(rule.min_bbox_area ?? 0),
    description: rule.description || ''
  })
  dialogVisible.value = true
}

async function handleSubmit() {
  if (!formRef.value) return
  await formRef.value.validate(async (valid) => {
    if (!valid) return
    submitting.value = true
    const payload = {
      name: formState.name,
      event_type: formState.event_type,
      enabled: formState.enabled,
      min_confidence: formState.min_confidence,
      severity: formState.severity || null,
      allowed_sources: formState.allowed_sources?.length ? formState.allowed_sources : null,
      required_labels: formState.required_labels?.length ? formState.required_labels : null,
      min_bbox_area: formState.min_bbox_area || 0,
      description: formState.description || ''
    }
    try {
      if (editingName.value) {
        await ruleApi.update(editingName.value, payload)
        ElMessage.success('规则已保存')
      } else {
        await ruleApi.create(payload)
        ElMessage.success('规则已创建')
      }
      dialogVisible.value = false
      await loadRules()
    } catch (err) {
      const msg = err.response?.data?.detail || err.message || String(err)
      ElMessage.error(`操作失败: ${msg}`)
    } finally {
      submitting.value = false
    }
  })
}

async function handleToggle(rule, enabled) {
  try {
    await ruleApi.update(rule.name, { ...rule, enabled })
    ElMessage.success(`已${enabled ? '启用' : '停用'} ${rule.name}`)
    await loadRules()
  } catch (err) {
    const msg = err.response?.data?.detail || err.message
    ElMessage.error(`切换失败: ${msg}`)
    await loadRules()
  }
}

async function handleDelete(rule) {
  try {
    await ruleApi.remove(rule.name)
    ElMessage.success('已删除')
    await loadRules()
  } catch (err) {
    ElMessage.error(`删除失败: ${err.response?.data?.detail || err.message}`)
  }
}

async function handleReload() {
  try {
    await ruleApi.reload()
    ElMessage.success('规则已热重载')
    await loadRules()
  } catch (err) {
    ElMessage.error(`重载失败: ${err.message}`)
  }
}

async function handleLLMEnable() {
  try {
    await llmApi.enable()
    ElMessage.success('LLM 已重新启用')
    await loadLLMStatus()
  } catch (err) {
    ElMessage.error(`操作失败: ${err.message}`)
  }
}

async function handleLLMDisable() {
  try {
    await llmApi.disable()
    ElMessage.success('LLM 已停用')
    await loadLLMStatus()
  } catch (err) {
    ElMessage.error(`操作失败: ${err.message}`)
  }
}

async function handleLLMReset() {
  try {
    await llmApi.reset()
    ElMessage.success('成本统计已重置')
    await loadLLMStatus()
  } catch (err) {
    ElMessage.error(`操作失败: ${err.message}`)
  }
}

onMounted(() => {
  loadRules()
  loadLLMStatus()
  loadCameras()
  pollTimer = setInterval(loadLLMStatus, 10000)
})

onUnmounted(() => {
  if (pollTimer) clearInterval(pollTimer)
})
</script>

<style scoped>
.rule-config-page {
  padding: 24px;
  min-height: 100%;
  background: #020617;
}

/* ---- LLM 状态面板 ---- */
.llm-panel {
  background: #0F172A;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 12px;
  padding: 18px 20px;
  margin-bottom: 20px;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}

.panel-title {
  display: flex;
  align-items: center;
  gap: 10px;
  color: #F8FAFC;
  font-size: 15px;
  font-weight: 600;
}

.panel-actions {
  display: flex;
  gap: 8px;
}

.llm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
}

.llm-card {
  padding: 12px 14px;
  background: #131E33;
  border: 1px solid rgba(255, 255, 255, 0.04);
  border-radius: 8px;
}

.llm-label {
  font-size: 12px;
  color: #94A3B8;
  margin-bottom: 6px;
}

.llm-value {
  font-size: 18px;
  font-weight: 600;
  color: #F8FAFC;
  font-family: 'Fira Code', monospace;
}

.llm-sub {
  font-size: 11px;
  color: #64748B;
  margin-top: 4px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.budget-bar-wrap {
  display: inline-block;
  width: 80px;
  height: 4px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: 999px;
  overflow: hidden;
}

.budget-bar {
  display: block;
  height: 100%;
  background: #22C55E;
  transition: width 200ms ease;
}

.budget-bar.is-danger {
  background: #EF4444;
}

.llm-warning {
  margin-top: 12px;
  padding: 8px 12px;
  background: rgba(239, 68, 68, 0.1);
  border: 1px solid rgba(239, 68, 68, 0.3);
  border-radius: 8px;
  color: #FCA5A5;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* ---- 工具栏 ---- */
.toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  margin-bottom: 16px;
  background: #0F172A;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 12px;
  flex-wrap: wrap;
}

.toolbar-left {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
}

.toolbar-right {
  display: flex;
  gap: 8px;
}

.filter-control {
  width: 200px;
}

/* ---- 规则列表 ---- */
.rule-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rule-card {
  display: flex;
  background: #0F172A;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 12px;
  overflow: hidden;
  transition: border-color 200ms ease;
}

.rule-card:hover {
  border-color: rgba(34, 197, 94, 0.3);
}

.rule-card.is-disabled {
  opacity: 0.55;
}

.rule-bar {
  width: 4px;
  flex-shrink: 0;
  background: #64748B;
}

.rule-bar.severity-critical { background: #EF4444; }
.rule-bar.severity-high     { background: #F59E0B; }
.rule-bar.severity-medium   { background: #3B82F6; }
.rule-bar.severity-low      { background: #94A3B8; }
.rule-bar.severity-info     { background: #22C55E; }

.rule-body {
  flex: 1;
  padding: 14px 16px;
  min-width: 0;
}

.rule-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}

.rule-title {
  display: flex;
  align-items: center;
  gap: 10px;
}

.rule-name {
  font-size: 15px;
  font-weight: 600;
  color: #F8FAFC;
  font-family: 'Fira Code', monospace;
}

.rule-actions {
  display: flex;
  gap: 4px;
}

.rule-desc {
  font-size: 13px;
  color: #94A3B8;
  margin-bottom: 8px;
}

.rule-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.meta-item {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: #94A3B8;
}

.meta-item .el-icon { font-size: 14px; }

.empty-state { padding: 60px 0; }

.form-hint {
  margin-left: 12px;
  font-size: 12px;
  color: #64748B;
}

:deep(.rule-dialog .el-dialog) {
  background: #0F172A;
  border: 1px solid rgba(255, 255, 255, 0.06);
}

:deep(.rule-dialog .el-dialog__title) { color: #F8FAFC; }
:deep(.rule-dialog .el-form-item__label) { color: #CBD5E1; }
</style>
