import { useState, type CSSProperties, type ReactNode } from 'react'
import { useNavigate } from 'react-router-dom'
import {
  Alert, Button, Table, Tag, Form, Select, InputNumber, Input, Space, Tooltip,
  Popconfirm, Drawer, Row, Col, Progress, Empty, Spin, message, Switch,
} from 'antd'
import {
  PlusOutlined, ReloadOutlined, StopOutlined, BarChartOutlined,
  FileMarkdownOutlined, MinusCircleOutlined, QuestionCircleOutlined,
  RocketOutlined, CheckCircleOutlined, SafetyOutlined, ClockCircleOutlined,
  RobotOutlined, SwapOutlined, CompassOutlined,
} from '@ant-design/icons'
import { Line, Bar } from '@ant-design/charts'
import FormDrawer from '../components/FormDrawer'
import PageWrapper from '../components/PageWrapper'
import StatCard from '../components/StatCard'
import {
  campaignsApi, targetsApi, scenariosApi, modelConfigsApi,
  type CampaignListItem, type Target, type Scenario, type Run,
  type ModelConfig,
} from '../api'
import { passRateColor } from '../utils/colors'
import { shortDateTime, toDate } from '../utils/date'
import { deriveTimeScale, acceleratedDuration, formatScale } from '../utils/campaignTime'
import WindowTimeline, { type TimelineMarker } from '../components/WindowTimeline'
import CampaignRunTimeline from '../components/CampaignRunTimeline'
import PeriodComparisonSection from '../components/PeriodComparisonSection'
import ExplorationSection from '../components/ExplorationSection'
import { useResource } from '../hooks/useResource'
import { useCampaignReport } from '../hooks/useCampaignReport'
import { useTabStore } from '../stores/tabStore'
import { colors, statusColors, statusLabels, triggerColors, triggerLabels } from '../tokens'

const CAMPAIGN_STATUS: Record<string, { label: string; color: string }> = {
  planned: { label: '计划中', color: 'default' },
  running: { label: '进行中', color: 'processing' },
  completed: { label: '已完成', color: 'success' },
  cancelled: { label: '已取消', color: 'default' },
  failed: { label: '失败', color: 'error' },
}

const SEVERITY_META: Record<string, { label: string; color: string }> = {
  high: { label: '高', color: 'red' },
  medium: { label: '中', color: 'orange' },
  low: { label: '低', color: 'blue' },
}

const WINDOW_OPTIONS = [6, 12, 24, 48, 72].map((h) => ({ label: `${h} 小时`, value: h * 3600 }))

const UNIT_OPTIONS = [
  { label: '分钟', value: 60 },
  { label: '小时', value: 3600 },
]

/** Single口径 for turning the reverse inputs into the backend's raw time_scale. */
function scaleFor(realtime: boolean, windowSeconds: number, targetValue: number, targetUnit: number): number {
  return realtime ? 1 : deriveTimeScale(windowSeconds, targetValue * targetUnit)
}

const isActiveStatus = (status: string) => status === 'planned' || status === 'running'

/** How far an active campaign's window has progressed, in window seconds. */
function nowOffsetFor(c: {
  status: string
  started_at: string | null
  time_scale: number
  window_seconds: number
}): number | null {
  if (!isActiveStatus(c.status) || !c.started_at) return null
  const elapsed = (Date.now() - toDate(c.started_at).getTime()) / 1000
  return Math.min(Math.max(elapsed * c.time_scale, 0), c.window_seconds)
}

function rateColorName(rate: number | null): 'blue' | 'green' | 'orange' | 'red' {
  if (rate == null) return 'blue'
  if (rate >= 0.8) return 'green'
  if (rate >= 0.6) return 'orange'
  return 'red'
}

function SectionTitle({ children }: { children: ReactNode }) {
  return <div style={{ fontWeight: 600, fontSize: 13, margin: '16px 0 12px' }}>{children}</div>
}

function SeedListText({ name, label, placeholder, addLabel }: {
  name: string
  label: string
  placeholder: string
  addLabel: string
}) {
  return (
    <>
      <div style={{ marginBottom: 6, fontSize: 12, color: colors.textSecondary }}>{label}</div>
      <Form.List name={name}>
        {(fields, { add, remove }) => (
          <>
            {fields.map((field) => (
              <div key={field.key} style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
                <Form.Item name={field.name} style={{ flex: 1, marginBottom: 0 }}>
                  <Input placeholder={placeholder} />
                </Form.Item>
                <div style={{ width: 16, lineHeight: '32px' }}>
                  <MinusCircleOutlined onClick={() => remove(field.name)} style={{ color: colors.textMuted }} />
                </div>
              </div>
            ))}
            <Button type="dashed" size="small" onClick={() => add('')} block icon={<PlusOutlined />}>
              {addLabel}
            </Button>
          </>
        )}
      </Form.List>
    </>
  )
}

function fmtWindow(seconds: number): string {
  if (seconds % 3600 === 0) return `${seconds / 3600}h`
  if (seconds % 60 === 0) return `${seconds / 60}m`
  return `${seconds}s`
}

function fmtPct(v: number | null | undefined): string {
  return v == null ? '—' : `${(v * 100).toFixed(1)}%`
}

interface PlanFormEntry {
  scenario_id?: string
  offset_hours?: number
  count?: number
}

interface CampaignAuxiliaryData {
  targets: Target[]
  scenarios: Scenario[]
  modelConfigs: ModelConfig[]
}

export default function CampaignsPage() {
  const navigate = useNavigate()
  const activeKey = useTabStore((s) => s.activeKey)

  const [createOpen, setCreateOpen] = useState(false)
  const [submitting, setSubmitting] = useState(false)
  const [form] = Form.useForm()

  const wWindow = (Form.useWatch('window_seconds', form) as number | undefined) ?? 0
  const wRealtime = Form.useWatch('realtime', form) as boolean | undefined
  const wTargetValue = (Form.useWatch('target_value', form) as number | undefined) ?? 0
  const wTargetUnit = (Form.useWatch('target_unit', form) as number | undefined) ?? 60
  const derivedScale = scaleFor(!!wRealtime, wWindow, wTargetValue, wTargetUnit)

  const [reportOpen, setReportOpen] = useState(false)
  const [reportCampaignId, setReportCampaignId] = useState<string | null>(null)
  const targetName = (id: string) => targets.find((t) => t.id === id)?.name ?? id.slice(0, 8)

  const { data } = useResource<CampaignAuxiliaryData>(
    async () => {
      const [t, s, m] = await Promise.all([
        targetsApi.list(), scenariosApi.list(), modelConfigsApi.list(),
      ])
      return { targets: t.data, scenarios: s.data, modelConfigs: m.data }
    },
    { tabPath: '/campaigns' },
  )
  const targets = data?.targets ?? []
  const scenarios = data?.scenarios ?? []
  const chatConfigs = (data?.modelConfigs ?? []).filter((m) => m.enabled && m.capability === 'chat')
  const analysisDefault = chatConfigs.find((m) => m.is_analysis_default)
  const scenarioNames = Object.fromEntries(scenarios.map((s) => [s.id, s.name]))

  const {
    list: campaignList,
    report: reportSlot,
    reloadList,
    expandedIds,
    timelines,
    setTimelineExpanded,
    generateAnalysis,
    generateComparison,
    analysisBusy,
  } = useCampaignReport(
    reportOpen ? reportCampaignId : null,
    activeKey === '/campaigns',
  )
  const campaigns = campaignList.value
  const loading = campaignList.phase === 'loading'
  const reportSnapshot = reportSlot.value
  const report = reportSnapshot?.report ?? null
  const reportRuns = reportSnapshot?.runs ?? []
  const reportTimeline = reportSnapshot?.timeline ?? []
  const analysis = reportSnapshot?.analysis ?? null
  const comparison = reportSnapshot?.comparison ?? null
  const reportLoading = reportSlot.phase === 'loading'

  const wPlan = (Form.useWatch('plan', form) as PlanFormEntry[] | undefined) ?? []
  const planMarkers: TimelineMarker[] = wPlan
    .map((e, i) => ({ e, i }))
    .filter(({ e }) => e && e.scenario_id)
    .map(({ e, i }) => {
      const name = scenarioNames[e.scenario_id as string] ?? (e.scenario_id as string).slice(0, 8)
      const count = e.count ?? 1
      const offsetHours = e.offset_hours ?? 0
      return {
        key: `plan-${i}`,
        offsetSeconds: offsetHours * 3600,
        colorKey: e.scenario_id as string,
        tooltip: `${name} · +${offsetHours}h · ${count} 次`,
      }
    })

  const openCreate = () => {
    form.setFieldsValue({
      name: '', target_id: undefined, window_seconds: 24 * 3600,
      realtime: false, target_value: 60, target_unit: 60,
      plan: [{ scenario_id: undefined, offset_hours: 0, count: 1 }],
      analysis_model_config_id: null,
      seed_personas: [], seed_goals: [],
      exploration_max_sessions: undefined, exploration_max_turns: undefined,
      exploration_min_interval_minutes: undefined,
    })
    setCreateOpen(true)
  }

  const submitCreate = async () => {
    const values = await form.validateFields()
    const windowSeconds = values.window_seconds as number
    const time_scale = scaleFor(
      !!values.realtime, windowSeconds, values.target_value ?? 0, values.target_unit ?? 60,
    )
    setSubmitting(true)
    try {
      const personas = ((values.seed_personas as string[] | undefined) ?? [])
        .map((s) => s?.trim()).filter(Boolean) as string[]
      const goals = ((values.seed_goals as string[] | undefined) ?? [])
        .map((s) => s?.trim()).filter(Boolean) as string[]
      const budget: Record<string, number> = {}
      if (values.exploration_max_sessions != null) budget.max_sessions = values.exploration_max_sessions
      if (values.exploration_max_turns != null) budget.max_turns = values.exploration_max_turns
      if (values.exploration_min_interval_minutes != null) {
        budget.min_interval_seconds = values.exploration_min_interval_minutes * 60
      }
      await campaignsApi.create({
        name: values.name,
        target_id: values.target_id,
        window_seconds: windowSeconds,
        time_scale,
        plan: (values.plan as PlanFormEntry[]).map((e) => ({
          scenario_id: e.scenario_id as string,
          offset_seconds: Math.round((e.offset_hours ?? 0) * 3600),
          count: e.count ?? 1,
        })),
        analysis_model_config_id: (values.analysis_model_config_id as string | null) ?? null,
        exploration_seeds: personas.length || goals.length ? { personas, goals } : null,
        exploration_budget: Object.keys(budget).length ? budget : null,
      })
      message.success('评估活动已创建并开始调度')
      setCreateOpen(false)
      void reloadList()
    } finally {
      setSubmitting(false)
    }
  }

  const cancelCampaign = async (id: string) => {
    await campaignsApi.cancel(id)
    message.success('活动已取消')
    void reloadList()
  }

  const onExpandRow = (expanded: boolean, record: CampaignListItem) => {
    setTimelineExpanded(record.id, expanded)
  }

  const openReport = (campaign: CampaignListItem) => {
    setReportOpen(true)
    setReportCampaignId(campaign.id)
  }

  const columns = [
    { title: '名称', dataIndex: 'name', key: 'name' },
    { title: '评测对象', key: 'target', render: (_: unknown, c: CampaignListItem) => targetName(c.target_id) },
    { title: '窗口', key: 'window', render: (_: unknown, c: CampaignListItem) => fmtWindow(c.window_seconds) },
    {
      title: '加速后耗时', key: 'scale',
      render: (_: unknown, c: CampaignListItem) => acceleratedDuration(c.window_seconds, c.time_scale),
    },
    {
      title: '状态', key: 'status',
      render: (_: unknown, c: CampaignListItem) => {
        const meta = CAMPAIGN_STATUS[c.status] ?? { label: c.status, color: 'default' }
        return <Tag color={meta.color}>{meta.label}</Tag>
      },
    },
    {
      title: '进度', key: 'progress',
      render: (_: unknown, c: CampaignListItem) => `${c.progress.completed_runs}/${c.progress.planned_total}`,
    },
    {
      title: '整窗通过率', key: 'pass_rate',
      render: (_: unknown, c: CampaignListItem) => {
        const rate = c.progress.overall_pass_rate
        if (rate == null) return '—'
        return (
          <Progress
            percent={Math.round(rate * 100)} size="small" style={{ width: 120 }}
            strokeColor={passRateColor(rate)}
          />
        )
      },
    },
    {
      title: '操作', key: 'actions',
      render: (_: unknown, c: CampaignListItem) => (
        <Space>
          <Button size="small" icon={<BarChartOutlined />} onClick={() => openReport(c)}>报告</Button>
          {isActiveStatus(c.status) && (
            <Popconfirm title="取消该活动？已完成的子运行会保留。" onConfirm={() => cancelCampaign(c.id)}>
              <Button size="small" danger icon={<StopOutlined />}>取消</Button>
            </Popconfirm>
          )}
        </Space>
      ),
    },
  ]

  const trendData = (report?.time_trend ?? []).flatMap((b) => {
    const slot = `${(b.start_seconds / 3600).toFixed(1)}h`
    const rows: { slot: string; value: number; type: string; runs: number }[] = []
    if (b.pass_rate != null) {
      rows.push({ slot, value: Math.round(b.pass_rate * 100), type: '通过率', runs: b.run_count })
    }
    if (b.availability != null) {
      rows.push({ slot, value: Math.round(b.availability * 100), type: '可用性', runs: b.run_count })
    }
    return rows
  })

  const trendConfig = {
    data: trendData,
    xField: 'slot',
    yField: 'value',
    seriesField: 'type',
    smooth: true,
    height: 260,
    scale: {
      y: { domain: [0, 100] },
      color: { domain: ['通过率', '可用性'], range: [colors.primary, '#52c41a'] },
    },
    axis: { y: { labelFormatter: (v: string) => `${v}%` } },
    point: { size: 3 },
    tooltip: {
      formatter: (d: { value: number; runs: number; type: string }) => ({
        name: d.type,
        value: `${d.value}%（${d.runs} 次）`,
      }),
    },
  }

  // Ascending sort: after the transpose the best scenario lands on top.
  const capData = (report?.capability_summary ?? [])
    .filter((c) => c.pass_rate != null)
    .slice()
    .sort((a, b) => (a.pass_rate as number) - (b.pass_rate as number))
    .map((c) => ({
      name: c.scenario_name,
      value: Math.round((c.pass_rate as number) * 100),
      runs: c.run_count,
      availability: c.availability,
      latency: c.avg_latency_ms,
    }))

  const capConfig = {
    data: capData,
    xField: 'name',
    yField: 'value',
    coordinate: { transform: [{ type: 'transpose' }] },
    height: 260,
    color: colors.primary,
    scale: { y: { domain: [0, 100] } },
    axis: { y: { labelFormatter: (v: string) => `${v}%` } },
    tooltip: {
      formatter: (d: {
        name: string; value: number; runs: number
        availability: number | null; latency: number | null
      }) => ({
        name: d.name,
        value: `${d.value}% · ${d.runs} 次 · 可用性 ${fmtPct(d.availability)} · 时延 ${d.latency == null ? '—' : `${Math.round(d.latency)}ms`}`,
      }),
    },
  }

  const runColumns = [
    { title: '子运行', dataIndex: 'id', key: 'id', render: (id: string) => <code>{id.slice(0, 8)}</code> },
    {
      title: '场景', key: 'scenario',
      filters: Array.from(new Set(reportRuns.map((r) => r.scenario_id))).map((id) => ({
        text: scenarios.find((s) => s.id === id)?.name ?? id.slice(0, 8),
        value: id,
      })),
      onFilter: (v: React.Key | boolean, r: Run) => r.scenario_id === v,
      render: (_: unknown, r: Run) => (
        <Space size={4}>
          <span>{scenarios.find((s) => s.id === r.scenario_id)?.name ?? r.scenario_id.slice(0, 8)}</span>
          {r.scenario_version != null && <Tag>v{r.scenario_version}</Tag>}
        </Space>
      ),
    },
    {
      title: '来源', key: 'trigger',
      render: (_: unknown, r: Run) => {
        const t = r.triggered_by ?? ''
        return <Tag color={triggerColors[t] ?? 'default'}>{triggerLabels[t] ?? t}</Tag>
      },
    },
    {
      title: '状态', key: 'status',
      filters: Array.from(new Set(reportRuns.map((r) => r.status))).map((s) => ({
        text: statusLabels[s] ?? s,
        value: s,
      })),
      onFilter: (v: React.Key | boolean, r: Run) => r.status === v,
      render: (_: unknown, r: Run) => (
        <span style={{ color: statusColors[r.status] ?? colors.textMuted }}>
          {statusLabels[r.status] ?? r.status}
        </span>
      ),
    },
    {
      title: '通过率', key: 'pass_rate',
      sorter: (a: Run, b: Run) => (a.summary?.pass_rate ?? -1) - (b.summary?.pass_rate ?? -1),
      render: (_: unknown, r: Run) => {
        const rate = r.summary?.pass_rate
        if (rate == null) return '—'
        return <span style={{ color: passRateColor(rate), fontWeight: 500 }}>{fmtPct(rate)}</span>
      },
    },
    {
      title: '时延', key: 'latency',
      sorter: (a: Run, b: Run) => (a.summary?.avg_latency_ms ?? -1) - (b.summary?.avg_latency_ms ?? -1),
      render: (_: unknown, r: Run) => {
        const ms = r.summary?.avg_latency_ms
        return ms == null ? '—' : `${Math.round(ms)}ms`
      },
    },
    {
      title: '时间', key: 'time',
      sorter: (a: Run, b: Run) => (a.started_at ?? '').localeCompare(b.started_at ?? ''),
      defaultSortOrder: 'descend' as const,
      render: (_: unknown, r: Run) => shortDateTime(r.started_at),
    },
    {
      title: '', key: 'drill',
      render: (_: unknown, r: Run) => (
        <Button size="small" type="link" onClick={() => navigate(`/reports?run=${r.id}`)}>查看报告</Button>
      ),
    },
  ]

  const renderCampaignTimeline = (c: CampaignListItem) => {
    const entries = timelines[c.id]
    if (entries === undefined) return <Spin />
    if (entries.length === 0) return <Empty description="暂无子运行" image={Empty.PRESENTED_IMAGE_SIMPLE} />
    return (
      <CampaignRunTimeline
        windowSeconds={c.window_seconds}
        entries={entries}
        nowOffsetSeconds={nowOffsetFor(c)}
      />
    )
  }

  // ── 智能分析区块 ─────────────────────────────────────────────────────
  const reportCampaign = campaigns.find((c) => c.id === reportCampaignId)
  const analysisTerminal = !!report && !isActiveStatus(report.status)
  const analysisModelMissing = !reportCampaign?.analysis_model_config_id && !analysisDefault
  const analysisModelName = analysis?.model_config_id
    ? (data?.modelConfigs ?? []).find((m) => m.id === analysis.model_config_id)?.name
      ?? analysis.model_config_id.slice(0, 8)
    : null

  const analysisButton = (label: string) => (
    <Tooltip title={analysisTerminal ? undefined : '活动完成后可生成'}>
      <Button
        size="small" type="primary" ghost loading={analysisBusy}
        disabled={!analysisTerminal} onClick={() => void generateAnalysis()}
      >
        {label}
      </Button>
    </Tooltip>
  )

  const analysisBoxStyle: CSSProperties = {
    border: `1px solid ${colors.border}`, borderRadius: 8,
    padding: '12px 14px', marginBottom: 16,
  }

  const renderAnalysisSection = () => {
    const status = analysis?.status ?? 'none'
    if (status === 'generating') {
      return (
        <div style={analysisBoxStyle}>
          <Spin size="small" />
          <span style={{ marginLeft: 10, color: colors.textSecondary }}>
            正在生成智能分析报告，通常需要几十秒…
          </span>
        </div>
      )
    }
    if (status === 'failed') {
      return (
        <div style={{ marginBottom: 16 }}>
          <Alert
            type="error" showIcon message="智能分析生成失败"
            description={analysis?.error || '未知错误'}
            action={analysisButton('重试')}
          />
        </div>
      )
    }
    const result = status === 'completed' ? analysis?.result : null
    if (!result) {
      return (
        <div style={{ ...analysisBoxStyle, background: colors.bgSubtle }}>
          <div style={{ color: colors.textSecondary, marginBottom: 8 }}>
            尚未生成智能分析报告。生成后将在此展示总体结论、问题诊断、分场景叙述与改善建议。
          </div>
          <Space size={12} wrap>
            {analysisButton('生成分析')}
            {analysisModelMissing && (
              <span style={{ fontSize: 12, color: colors.warning }}>
                未配置分析模型：请先在模型配置中心将某个 chat 配置设为「分析默认」，或重新创建活动时指定
              </span>
            )}
          </Space>
        </div>
      )
    }
    return (
      <div style={analysisBoxStyle}>
        <div style={{
          display: 'flex', justifyContent: 'space-between', alignItems: 'center',
          fontSize: 12, color: colors.textSecondary, marginBottom: 10,
        }}>
          <span>
            分析模型 {analysisModelName ?? '未知'}
            {analysis?.updated_at && <> · 生成于 {shortDateTime(analysis.updated_at)}</>}
          </span>
          {analysisButton('重新生成')}
        </div>
        <div style={{
          background: '#e6f4ff', border: '1px solid #91caff', borderRadius: 8,
          padding: '10px 14px', marginBottom: 12,
        }}>
          <b>总体结论：</b>{result.overall}
        </div>
        {result.problems.length > 0 && (
          <div style={{ marginBottom: 12 }}>
            <div style={{ fontWeight: 500, marginBottom: 8 }}>问题诊断</div>
            <Space direction="vertical" size={8} style={{ width: '100%' }}>
              {result.problems.map((p, i) => {
                const meta = SEVERITY_META[p.severity] ?? SEVERITY_META.medium
                return (
                  <div key={i} style={{ border: `1px solid ${colors.border}`, borderRadius: 8, padding: '8px 12px' }}>
                    <Space size={8} wrap>
                      <Tag color={meta.color} style={{ margin: 0 }}>{meta.label}</Tag>
                      <b>{p.title}</b>
                      {p.scenario_ids.map((sid) => (
                        <Tag key={sid} style={{ margin: 0 }}>{scenarioNames[sid] ?? sid.slice(0, 8)}</Tag>
                      ))}
                    </Space>
                    <div style={{ color: colors.textSecondary, margin: '6px 0' }}>{p.description}</div>
                    {p.evidence_run_ids.length > 0 && (
                      <Space size={4} wrap>
                        <span style={{ fontSize: 12, color: colors.textMuted }}>证据：</span>
                        {p.evidence_run_ids.map((rid) => (
                          <Tag
                            key={rid} color="blue" style={{ cursor: 'pointer', margin: 0 }}
                            onClick={() => navigate(`/reports?run=${rid}`)}
                          >
                            {rid.slice(0, 8)}
                          </Tag>
                        ))}
                      </Space>
                    )}
                  </div>
                )
              })}
            </Space>
          </div>
        )}
        {result.scenario_narratives.length > 0 && (
          <div style={{ marginBottom: 12 }}>
            <div style={{ fontWeight: 500, marginBottom: 8 }}>分场景叙述</div>
            {result.scenario_narratives.map((n) => (
              <div key={n.scenario_id} style={{ marginBottom: 6 }}>
                <Tag>{scenarioNames[n.scenario_id] ?? n.scenario_id.slice(0, 8)}</Tag>
                <span>{n.narrative}</span>
              </div>
            ))}
          </div>
        )}
        {result.suggestions.length > 0 && (
          <div>
            <div style={{ fontWeight: 500, marginBottom: 8 }}>改善建议</div>
            <ol style={{ margin: 0, paddingLeft: 20 }}>
              {[...result.suggestions]
                .sort((a, b) => a.priority - b.priority)
                .map((s, i) => <li key={i} style={{ marginBottom: 4 }}>{s.text}</li>)}
            </ol>
          </div>
        )}
      </div>
    )
  }

  return (
    <PageWrapper
      title="评估活动"
      description="在一个服务周期窗口内按计划持续评测同一对象，聚合成周期报告"
      inline
      fullHeight
      extra={
        <Space>
          <Button icon={<ReloadOutlined />} onClick={() => void reloadList()} />
          <Button type="primary" icon={<PlusOutlined />} onClick={openCreate}>新建活动</Button>
        </Space>
      }
    >
      <div style={{ height: '100%', overflowY: 'auto', padding: '0 16px 16px' }}>
        <Table
          rowKey="id"
          loading={loading}
          dataSource={campaigns}
          columns={columns}
          pagination={campaigns.length > 20 ? { pageSize: 20, showTotal: (t) => `共 ${t} 个` } : false}
          expandable={{
            expandedRowKeys: expandedIds,
            onExpand: onExpandRow,
            expandedRowRender: renderCampaignTimeline,
          }}
          locale={{ emptyText: <Empty description="还没有评估活动" /> }}
        />
      </div>

      {/* 创建活动 */}
      <FormDrawer
        title="新建评估活动"
        open={createOpen}
        onClose={() => setCreateOpen(false)}
        onSubmit={submitCreate}
        submitting={submitting}
        okText="创建并开始调度"
        width={920}
      >
        <Form form={form} layout="vertical">
          <Row gutter={32}>
            <Col span={9}>
              <SectionTitle>基本信息</SectionTitle>
              <Form.Item name="name" label="活动名称" rules={[{ required: true, message: '请输入名称' }]}>
                <Input placeholder="如：数字员工 24h 服务周期" />
              </Form.Item>
              <Form.Item name="target_id" label="评测对象" rules={[{ required: true, message: '请选择对象' }]}>
                <Select
                  placeholder="选择评测对象"
                  showSearch
                  optionFilterProp="label"
                  options={targets.map((t) => ({ label: t.name, value: t.id }))}
                />
              </Form.Item>

              <SectionTitle>时间与速度</SectionTitle>
              <Form.Item
                name="window_seconds"
                label="窗口长度"
                tooltip="服务对象的一个完整服务周期（如早 8 点到次日早 8 点 = 24 小时），所有计划条目都落在窗口内"
                rules={[{ required: true }]}
              >
                <Select
                  options={WINDOW_OPTIONS}
                  onChange={() => {
                    // Shrinking the window can invalidate existing plan offsets —
                    // re-validate them so the errors surface immediately.
                    const plan = (form.getFieldValue('plan') as PlanFormEntry[] | undefined) ?? []
                    form.validateFields(plan.map((_, i) => ['plan', i, 'offset_hours'])).catch(() => {})
                  }}
                />
              </Form.Item>
              <Form.Item
                name="realtime"
                label="实时（真实墙钟）"
                tooltip="开启后按真实时间推进（倍速 ×1，正式线用）；关闭则压缩窗口，加速跑完"
                valuePropName="checked"
              >
                <Switch checkedChildren="实时" unCheckedChildren="加速" />
              </Form.Item>
              <Form.Item
                label="希望加速后多久跑完"
                tooltip="按“耗时”思考：例如 24 小时窗口希望 1 小时内跑完 → 自动换算为 ×24"
                required={!wRealtime}
              >
                <Space align="baseline">
                  <Form.Item
                    name="target_value"
                    noStyle
                    rules={[{
                      validator: async (_, v: number | undefined) => {
                        if (form.getFieldValue('realtime')) return
                        if (!v || v <= 0) throw new Error('请输入目标耗时')
                        const unit = (form.getFieldValue('target_unit') as number | undefined) ?? 60
                        const win = (form.getFieldValue('window_seconds') as number | undefined) ?? 0
                        if (v * unit > win) throw new Error('目标耗时不能超过窗口长度（否则倍速 < 1）')
                      },
                    }]}
                  >
                    <InputNumber min={0.1} step={1} disabled={wRealtime} style={{ width: 140 }} />
                  </Form.Item>
                  <Form.Item name="target_unit" noStyle>
                    <Select options={UNIT_OPTIONS} disabled={wRealtime} style={{ width: 90 }} />
                  </Form.Item>
                </Space>
              </Form.Item>
              <div style={{
                background: '#e6f4ff', border: '1px solid #91caff', borderRadius: 8,
                padding: '8px 12px', fontSize: 12, color: colors.text,
              }}>
                {wRealtime
                  ? `实时模式：按真实墙钟推进，计划贯穿整个 ${fmtWindow(wWindow)} 窗口`
                  : (
                    <>
                      窗口 {fmtWindow(wWindow)} · 倍速 <b>{formatScale(derivedScale)}</b>
                      {' · '}{acceleratedDuration(wWindow, derivedScale)}
                    </>
                  )}
              </div>
              <Form.Item
                name="analysis_model_config_id"
                label="分析模型"
                tooltip="活动完成后生成智能分析报告使用的模型；不选则跟随模型配置中心的「分析默认」"
                style={{ marginTop: 16, marginBottom: 0 }}
              >
                <Select
                  showSearch
                  optionFilterProp="label"
                  options={[
                    {
                      label: analysisDefault
                        ? `全局默认（${analysisDefault.name}）`
                        : '全局默认（未配置）',
                      value: null,
                    },
                    ...chatConfigs.map((m) => ({ label: m.name, value: m.id as string | null })),
                  ]}
                />
              </Form.Item>
            </Col>

            <Col span={15}>
              <SectionTitle>计划预览</SectionTitle>
              <div style={{
                border: `1px solid ${colors.border}`, borderRadius: 8,
                padding: '12px 8px', marginBottom: 16,
              }}>
                {planMarkers.length > 0 ? (
                  <WindowTimeline
                    windowSeconds={wWindow}
                    markers={planMarkers}
                    colorLabels={scenarioNames}
                    showLegend
                    stagger
                  />
                ) : (
                  <div style={{ textAlign: 'center', color: colors.textMuted, padding: '12px 0' }}>
                    选择场景并设置开始时间后，将在此按时间轴预览计划
                  </div>
                )}
              </div>

              <SectionTitle>计划条目</SectionTitle>
              <div style={{ marginBottom: 8, fontSize: 12, color: colors.textSecondary }}>
                在窗口的哪些时间点跑哪个场景、跑几次
              </div>
              <div style={{ display: 'flex', gap: 8, marginBottom: 6, fontSize: 12, color: colors.textSecondary }}>
                <span style={{ flex: 1 }}>场景</span>
                <span style={{ width: 140 }}>
                  开始时间{' '}
                  <Tooltip title="该场景在窗口开始后第几个小时启动">
                    <QuestionCircleOutlined style={{ color: colors.textMuted }} />
                  </Tooltip>
                </span>
                <span style={{ width: 104 }}>执行次数</span>
                <span style={{ width: 16 }} />
              </div>
              <Form.List name="plan" rules={[{ validator: async (_, plan) => { if (!plan || plan.length < 1) return Promise.reject(new Error('至少一条计划条目')) } }]}>
                {(fields, { add, remove }, { errors }) => (
                  <>
                    <div style={{ maxHeight: 360, overflowY: 'auto' }}>
                      {fields.map((field) => (
                        <div key={field.key} style={{ display: 'flex', gap: 8, alignItems: 'flex-start', marginBottom: 8 }}>
                          <Form.Item
                            name={[field.name, 'scenario_id']}
                            rules={[{ required: true, message: '选择场景' }]}
                            style={{ flex: 1, marginBottom: 0 }}
                          >
                            <Select
                              placeholder="选择场景"
                              showSearch
                              optionFilterProp="label"
                              options={scenarios.map((s) => ({ label: s.name, value: s.id }))}
                            />
                          </Form.Item>
                          <Form.Item
                            name={[field.name, 'offset_hours']}
                            style={{ width: 140, marginBottom: 0 }}
                            rules={[{
                              validator: async (_, v: number | undefined) => {
                                if (v == null) return
                                const win = (form.getFieldValue('window_seconds') as number | undefined) ?? 0
                                if (win > 0 && v * 3600 > win) {
                                  throw new Error(`不能超过窗口长度（${fmtWindow(win)}）`)
                                }
                              },
                            }]}
                          >
                            <InputNumber
                              min={0} step={0.5} addonAfter="小时后"
                              max={wWindow > 0 ? wWindow / 3600 : undefined}
                              style={{ width: '100%' }}
                            />
                          </Form.Item>
                          <Form.Item name={[field.name, 'count']} style={{ width: 104, marginBottom: 0 }}>
                            <InputNumber min={1} addonAfter="次" style={{ width: '100%' }} />
                          </Form.Item>
                          <div style={{ width: 16, lineHeight: '32px' }}>
                            {fields.length > 1 && (
                              <MinusCircleOutlined onClick={() => remove(field.name)} style={{ color: colors.textMuted }} />
                            )}
                          </div>
                        </div>
                      ))}
                    </div>
                    <Button type="dashed" onClick={() => add({ scenario_id: undefined, offset_hours: 0, count: 1 })} block icon={<PlusOutlined />}>
                      添加计划条目
                    </Button>
                    <Form.ErrorList errors={errors} />
                  </>
                )}
              </Form.List>

              <SectionTitle>
                探索式评测（虚拟用户）{' '}
                <Tooltip title="AI 助手以种子人设 × 种子目标自主与被评对象对话；种子留空则该活动不参与探索">
                  <QuestionCircleOutlined style={{ color: colors.textMuted }} />
                </Tooltip>
              </SectionTitle>
              <Row gutter={16}>
                <Col span={12}>
                  <SeedListText name="seed_personas" label="种子人设" placeholder="如：急性子的缴费用户" addLabel="添加人设" />
                </Col>
                <Col span={12}>
                  <SeedListText name="seed_goals" label="种子目标" placeholder="如：查询本月账单并完成缴费" addLabel="添加目标" />
                </Col>
              </Row>
              <div style={{ marginTop: 12, marginBottom: 6, fontSize: 12, color: colors.textSecondary }}>
                探索预算覆盖（留空使用平台默认：8 会话/窗口 · 12 轮/会话 · 30 分钟间隔）
              </div>
              <div style={{ display: 'flex', gap: 8 }}>
                <Form.Item name="exploration_max_sessions" style={{ flex: 1, marginBottom: 0 }}>
                  <InputNumber min={1} placeholder="默认 8" addonAfter="会话/窗口" style={{ width: '100%' }} />
                </Form.Item>
                <Form.Item name="exploration_max_turns" style={{ flex: 1, marginBottom: 0 }}>
                  <InputNumber min={1} placeholder="默认 12" addonAfter="轮/会话" style={{ width: '100%' }} />
                </Form.Item>
                <Form.Item name="exploration_min_interval_minutes" style={{ flex: 1, marginBottom: 0 }}>
                  <InputNumber min={1} placeholder="默认 30" addonAfter="分钟间隔" style={{ width: '100%' }} />
                </Form.Item>
              </div>
            </Col>
          </Row>
        </Form>
      </FormDrawer>

      {/* 周期报告 */}
      <Drawer
        title={report ? `活动报告 — ${report.name}` : '活动报告'}
        open={reportOpen}
        onClose={() => setReportOpen(false)}
        width={1040}
        extra={
          report && (
            <Button icon={<FileMarkdownOutlined />} onClick={() => campaignsApi.downloadReport(report.campaign_id)}>
              导出 Markdown
            </Button>
          )
        }
      >
        <Spin spinning={reportLoading}>
          {report && (
            <>
              <Row gutter={12}>
                <Col span={6}>
                  <StatCard
                    icon={<RocketOutlined />}
                    color="blue"
                    title="子运行"
                    value={`${report.summary.completed_runs}/${report.summary.total_runs}`}
                    subText={`状态：${CAMPAIGN_STATUS[report.status]?.label ?? report.status}`}
                  />
                </Col>
                <Col span={6}>
                  <StatCard
                    icon={<CheckCircleOutlined />}
                    color={rateColorName(report.summary.overall_pass_rate)}
                    title="整窗通过率"
                    value={report.summary.overall_pass_rate == null ? '—' : (report.summary.overall_pass_rate * 100).toFixed(1)}
                    suffix={report.summary.overall_pass_rate == null ? undefined : '%'}
                  />
                </Col>
                <Col span={6}>
                  <StatCard
                    icon={<SafetyOutlined />}
                    color={rateColorName(report.summary.overall_availability)}
                    title="可用性"
                    value={report.summary.overall_availability == null ? '—' : (report.summary.overall_availability * 100).toFixed(1)}
                    suffix={report.summary.overall_availability == null ? undefined : '%'}
                  />
                </Col>
                <Col span={6}>
                  <StatCard
                    icon={<ClockCircleOutlined />}
                    color="orange"
                    title="平均时延"
                    value={report.summary.avg_latency_ms == null ? '—' : Math.round(report.summary.avg_latency_ms)}
                    suffix={report.summary.avg_latency_ms == null ? undefined : 'ms'}
                  />
                </Col>
              </Row>
              <div style={{ margin: '8px 0 4px', fontSize: 12, color: colors.textSecondary }}>
                窗口 {fmtWindow(report.window_seconds)} · {acceleratedDuration(report.window_seconds, report.time_scale)}
                {' · '}开始于 {shortDateTime(report.started_at)}
              </div>

              <SectionTitle><RobotOutlined /> 智能分析</SectionTitle>
              {renderAnalysisSection()}

              <SectionTitle><SwapOutlined /> 周期对比</SectionTitle>
              <PeriodComparisonSection
                key={report.campaign_id}
                comparison={comparison}
                terminal={analysisTerminal}
                analysisCompleted={analysis?.status === 'completed'}
                targetId={report.target_id}
                currentCampaignId={report.campaign_id}
                campaigns={campaigns}
                scenarioNames={scenarioNames}
                modelConfigs={data?.modelConfigs ?? []}
                onGenerate={generateComparison}
              />

              {report.exploration && (
                <>
                  <SectionTitle><CompassOutlined /> 探索发现</SectionTitle>
                  <ExplorationSection
                    key={report.campaign_id}
                    campaignId={report.campaign_id}
                    summary={report.exploration}
                  />
                </>
              )}

              <SectionTitle>过程时间轴</SectionTitle>
              <div style={{
                border: `1px solid ${colors.border}`, borderRadius: 8,
                padding: '12px 8px 8px', marginBottom: 16,
              }}>
                {reportTimeline.length > 0 ? (
                  <CampaignRunTimeline
                    windowSeconds={report.window_seconds}
                    entries={reportTimeline}
                    nowOffsetSeconds={nowOffsetFor(report)}
                  />
                ) : (
                  <div style={{ textAlign: 'center', color: colors.textMuted, padding: '12px 0' }}>
                    暂无子运行
                  </div>
                )}
              </div>

              <Row gutter={16}>
                <Col span={12}>
                  <SectionTitle>时间趋势（通过率 / 可用性）</SectionTitle>
                  <div style={{ border: `1px solid ${colors.border}`, borderRadius: 8, padding: 12 }}>
                    {trendData.length > 0
                      ? <Line {...trendConfig} />
                      : <Empty description="暂无已完成子运行" image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                  </div>
                </Col>
                <Col span={12}>
                  <SectionTitle>能力排行（按通过率）</SectionTitle>
                  <div style={{ border: `1px solid ${colors.border}`, borderRadius: 8, padding: 12 }}>
                    {capData.length > 0
                      ? <Bar {...capConfig} />
                      : <Empty description="暂无已完成场景数据" image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                  </div>
                </Col>
              </Row>

              <SectionTitle>子运行</SectionTitle>
              <Table
                rowKey="id"
                size="small"
                pagination={reportRuns.length > 20 ? { pageSize: 20, showTotal: (t) => `共 ${t} 个` } : false}
                dataSource={reportRuns}
                columns={runColumns}
                locale={{ emptyText: <Empty description="暂无子运行" /> }}
              />
            </>
          )}
        </Spin>
      </Drawer>
    </PageWrapper>
  )
}
