import type {
  ScheduledConditionAnalysisInsight,
  ScheduledConditionItem,
  ScheduledConditionKpi,
  ScheduledConditionReport,
  ScheduledConditionRiskLevel,
  ScheduledConditionStatus,
  ScheduledConditionTaskId,
  ScheduledWorkOrderStatus
} from "../types";

export const SCHEDULED_CONDITION_REFRESH_INTERVAL_MS = 5_000;

const HISTORY_WINDOW_MINUTES = 6 * 60;
const GENERATED_SESSION_ID_PREFIX = "scheduled-condition-";
const MODEL_NAME = "DrainFlow Realtime Simulator";
const GUARANTEED_RUNNING_TASK_ID: ConditionTaskId = "scada-diagnosis";

type ConditionTaskId = ScheduledConditionTaskId;
type WorkOrderPhase = "dispatch" | "execution" | "reply";

type ConditionTaskDefinition = {
  id: ConditionTaskId;
  title: string;
  summary: string;
  intervalMinutes: number;
  durationMinutes: number;
  activeWindow?: (minutesOfDay: number) => boolean;
};

type TaskSignal = {
  completeness: number;
  deviation: number;
  target: string;
  sampleCount: number;
  taskId: ConditionTaskId;
};

type ManualWorkOrderDefinition = {
  id: string;
  title: string;
  summary: string;
  detail: string;
  recommendation: string;
  source: string;
  location: string;
  dispatcher: string;
  assignee: string;
  priority: "normal" | "urgent";
  startMinute: number;
  durationMinutes: number;
  dispatchLeadMinutes: number;
  replyWindowMinutes: number;
  riskLevel: ScheduledConditionRiskLevel;
  replyRequirements: string[];
};

type MockWorkOrderTemplate = Omit<ManualWorkOrderDefinition, "startMinute" | "durationMinutes" | "dispatchLeadMinutes" | "replyWindowMinutes"> & {
  stage: "dispatch" | "execution" | "reply";
  durationMinutes: number;
  dispatchLeadMinutes: number;
  replyWindowMinutes: number;
};

type DispatchInstruction = {
  action: string;
  target: string;
  command: string;
  verification: string;
};

const CONDITION_TASKS: ConditionTaskDefinition[] = [
  {
    id: "scada-diagnosis",
    title: "SCADA 数据诊断",
    summary: "对压力、流量 SCADA 数据进行新鲜度、完整率、突变和一致性诊断，覆盖分区压力巡检范围。",
    intervalMinutes: 5,
    durationMinutes: 3
  },
  {
    id: "smart-dispatch",
    title: "管网智能调度",
    summary: "结合实时工况生成管网要素调整指令，输出泵站、阀门或分区边界的建议动作。",
    intervalMinutes: 15,
    durationMinutes: 4
  },
  {
    id: "network-simulation",
    title: "管网定时模拟",
    summary: "基于最新遥测、泵站启停和阀门开度滚动复核全网水力状态。",
    intervalMinutes: 15,
    durationMinutes: 6
  },
  {
    id: "pump-energy",
    title: "泵站能耗检查",
    summary: "检查泵组负载、单位供水电耗、启停频率和供压稳定性。",
    intervalMinutes: 60,
    durationMinutes: 10
  }
];

const MOCK_WORK_ORDER_TEMPLATES: MockWorkOrderTemplate[] = [
  {
    id: "mock-incident-valve-dispatch",
    title: "系统工单：华山路阀门边界确认",
    summary: "异常事件分析后生成阀门边界确认任务，派发前需调度员复核影响范围。",
    detail: "该工单由异常事件分析流程生成，用于演示人工确认后提交系统工单并通知管网要素操作。",
    recommendation: "确认影响用户、上下游压力和应急隔离边界后，再通知执行岗位到场。",
    source: "异常事件分析",
    location: "华山路沿线阀门组 VG-11",
    dispatcher: "调度中心值班长",
    assignee: "调度执行岗",
    priority: "urgent",
    durationMinutes: 40,
    dispatchLeadMinutes: 30,
    replyWindowMinutes: 20,
    riskLevel: "attention",
    stage: "dispatch",
    replyRequirements: ["确认阀门当前开度", "记录影响分区和用户范围", "回传派发确认截图"]
  },
  {
    id: "mock-valve-operation-execution",
    title: "系统工单：北辰分区阀门开度调整",
    summary: "根据管网智能调度方案，现场调整北辰分区边界阀门开度并复核压力。",
    detail: "该工单模拟系统派发后的执行阶段，班组正在按方案对阀门要素进行操作。",
    recommendation: "执行过程中保持调度语音联络，调整后立即复核末端压力和流量变化。",
    source: "调度方案确认",
    location: "北辰分区边界阀门 BV-07",
    dispatcher: "管网调度岗",
    assignee: "北辰抢修二组",
    priority: "urgent",
    durationMinutes: 55,
    dispatchLeadMinutes: 25,
    replyWindowMinutes: 20,
    riskLevel: "attention",
    stage: "execution",
    replyRequirements: ["上传阀门操作前后照片", "记录最终开度", "复核北辰末端压力不低于 0.22MPa"]
  },
  {
    id: "mock-pump-boundary-reply",
    title: "系统工单：泵站边界复核复令",
    summary: "泵站出水边界复核已完成，等待班组复令和调度确认闭环。",
    detail: "该工单模拟执行完成后的复令阶段，用于检查处置结果、照片和指标回传。",
    recommendation: "核对泵站出水压力、频率曲线和分区边界模拟结果后完成复令。",
    source: "模型复核建议",
    location: "北辰二级泵站 P-02",
    dispatcher: "模型复核岗",
    assignee: "泵站运行班组",
    priority: "normal",
    durationMinutes: 45,
    dispatchLeadMinutes: 20,
    replyWindowMinutes: 35,
    riskLevel: "normal",
    stage: "reply",
    replyRequirements: ["上传泵站运行截图", "填写出水压力复核结果", "确认模型边界已同步更新"]
  }
];

const MANUAL_WORK_ORDERS: ManualWorkOrderDefinition[] = [
  {
    id: "huashan-valve-well-service",
    title: "人工工单：华山路阀井现场处置",
    summary: "现场班组复核华山路阀井积水与井盖松动问题，完成照片和处置记录回传。",
    detail: "该工单由调度员人工创建，来源为现场巡检反馈，不属于周期工况任务。",
    recommendation: "到点后确认现场安全措施、处置照片和回填记录，必要时关联资产缺陷单。",
    source: "现场巡检反馈",
    location: "华山路阀井",
    dispatcher: "调度中心值班长",
    assignee: "北片区抢修一组",
    priority: "urgent",
    startMinute: 9 * 60 + 40,
    durationMinutes: 35,
    dispatchLeadMinutes: 20,
    replyWindowMinutes: 15,
    riskLevel: "attention",
    replyRequirements: ["上传处置前后照片", "记录阀井积水处理结果", "复核井盖密封和周边安全状态"]
  },
  {
    id: "renmin-road-meter-change",
    title: "人工工单：人民路流量计更换",
    summary: "更换人民路 DN300 支线流量计通信模块，并复测回传质量。",
    detail: "该工单由计量维护计划人工创建，执行前需确认旁通和作业窗口。",
    recommendation: "作业完成后核对 15 分钟回传稳定性，并记录新模块编号。",
    source: "计量维护计划",
    location: "人民路 DN300 支线",
    dispatcher: "计量调度岗",
    assignee: "计量维护二组",
    priority: "normal",
    startMinute: 10 * 60 + 30,
    durationMinutes: 50,
    dispatchLeadMinutes: 30,
    replyWindowMinutes: 20,
    riskLevel: "normal",
    replyRequirements: ["登记新通信模块编号", "回传 15 分钟稳定性截图", "确认旁通恢复和计量数据连续"]
  },
  {
    id: "beichen-fire-hydrant-repair",
    title: "人工工单：北辰消防栓维修",
    summary: "处理北辰片区消防栓渗漏缺陷，现场确认关停影响和恢复时间。",
    detail: "该工单来自客服缺陷转派，由调度员人工排入当日维修窗口。",
    recommendation: "维修前通知影响点位，完成后复核周边压力和栓体密封状态。",
    source: "客服缺陷转派",
    location: "北辰片区消防栓",
    dispatcher: "客服联动调度",
    assignee: "北辰维修班组",
    priority: "urgent",
    startMinute: 14 * 60,
    durationMinutes: 60,
    dispatchLeadMinutes: 45,
    replyWindowMinutes: 20,
    riskLevel: "attention",
    replyRequirements: ["回填关停影响范围", "上传维修完成照片", "复测周边压力和消防栓密封状态"]
  }
];

export function createOperationalScheduledConditions(now = new Date()): ScheduledConditionItem[] {
  const currentMinutesOfDay = getMinutesOfDay(now);
  const historyStart = Math.max(0, currentMinutesOfDay - HISTORY_WINDOW_MINUTES);
  const historicalConditions = CONDITION_TASKS.flatMap((task) =>
    createHistoricalConditionsForTask(task, now, historyStart, currentMinutesOfDay)
  );
  const runningCondition = createGuaranteedRunningCondition(now, currentMinutesOfDay);
  const operationalConditions =
    historicalConditions.some((condition) => condition.status === "running") || !runningCondition
      ? historicalConditions
      : [runningCondition, ...historicalConditions.filter((condition) => condition.id !== runningCondition.id)];
  const futureWorkOrders = createUpcomingWorkOrders(now, currentMinutesOfDay);

  return [...operationalConditions, ...futureWorkOrders].sort(
    (a, b) => Date.parse(b.scheduledAt) - Date.parse(a.scheduledAt)
  );
}

export function isGeneratedScheduledConditionSessionId(sessionId: string) {
  return sessionId.startsWith(GENERATED_SESSION_ID_PREFIX);
}

function createHistoricalConditionsForTask(
  task: ConditionTaskDefinition,
  now: Date,
  historyStart: number,
  currentMinutesOfDay: number
) {
  const firstMinute = ceilToInterval(historyStart, task.intervalMinutes);
  const lastMinute = floorToInterval(currentMinutesOfDay, task.intervalMinutes);
  const conditions: ScheduledConditionItem[] = [];

  for (let minute = firstMinute; minute <= lastMinute; minute += task.intervalMinutes) {
    if (task.activeWindow && !task.activeWindow(minute)) {
      continue;
    }

    const scheduledAtDate = setMinutesOfDay(now, minute);
    const scheduledAt = formatLocalIsoWithOffset(scheduledAtDate);
    const signal = createTaskSignal(task, scheduledAtDate);
    const status = getConditionStatus(task, scheduledAtDate, now, signal);
    const riskLevel = getRiskLevel(status, signal);
    const kpis = createTaskKpis(task, signal);
    const durationMinutes = getTaskExecutionDuration(task);

    conditions.push({
      id: `${GENERATED_SESSION_ID_PREFIX}${task.id}-${formatCompactLocalTime(scheduledAtDate)}`,
      kind: "condition",
      taskId: task.id,
      scheduledAt,
      title: task.title,
      summary: task.summary,
      status,
      riskLevel,
      sessionId: `${GENERATED_SESSION_ID_PREFIX}${task.id}-${formatCompactLocalTime(scheduledAtDate)}`,
      updatedAt: Math.min(now.getTime(), scheduledAtDate.getTime() + durationMinutes * 60_000),
      detail: createDetail(task, scheduledAtDate, status, signal),
      evidence: createEvidence(task, signal, status),
      recommendation: createRecommendation(status, riskLevel, task, signal),
      analysisInsight: createAnalysisInsight(task, status, riskLevel, signal),
      kpis,
      report: createConditionReport(task, status, riskLevel, signal, kpis),
      modelName: MODEL_NAME,
      durationMinutes
    });
  }

  return conditions;
}

function createGuaranteedRunningCondition(now: Date, currentMinutesOfDay: number): ScheduledConditionItem | null {
  const task = CONDITION_TASKS.find((item) => item.id === GUARANTEED_RUNNING_TASK_ID);
  if (!task) {
    return null;
  }

  const scheduledMinute = floorToInterval(currentMinutesOfDay, task.intervalMinutes);
  if (task.activeWindow && !task.activeWindow(scheduledMinute)) {
    return null;
  }

  const scheduledAtDate = setMinutesOfDay(now, scheduledMinute);
  const scheduledAt = formatLocalIsoWithOffset(scheduledAtDate);
  const signal = createTaskSignal(task, scheduledAtDate);
  const status: ScheduledConditionStatus = "running";
  const riskLevel = getRiskLevel(status, signal);
  const kpis = createTaskKpis(task, signal);
  const durationMinutes = getTaskExecutionDuration(task);

  return {
    id: `${GENERATED_SESSION_ID_PREFIX}${task.id}-${formatCompactLocalTime(scheduledAtDate)}`,
    kind: "condition",
    taskId: task.id,
    scheduledAt,
    title: task.title,
    summary: task.summary,
    status,
    riskLevel,
    sessionId: `${GENERATED_SESSION_ID_PREFIX}${task.id}-${formatCompactLocalTime(scheduledAtDate)}`,
    updatedAt: now.getTime(),
    detail: createDetail(task, scheduledAtDate, status, signal),
    evidence: createEvidence(task, signal, status),
    recommendation: createRecommendation(status, riskLevel, task, signal),
    analysisInsight: createAnalysisInsight(task, status, riskLevel, signal),
    kpis,
    report: createConditionReport(task, status, riskLevel, signal, kpis),
    modelName: MODEL_NAME,
    durationMinutes
  };
}

function createUpcomingWorkOrders(now: Date, currentMinutesOfDay: number): ScheduledConditionItem[] {
  const workOrders = [...MANUAL_WORK_ORDERS, ...createMockWorkOrders(currentMinutesOfDay)];

  return workOrders.filter(
    (workOrder) => currentMinutesOfDay <= workOrder.startMinute + workOrder.durationMinutes + workOrder.replyWindowMinutes
  )
    .map((workOrder) => {
      const scheduledAtDate = setMinutesOfDay(now, workOrder.startMinute);
      const replyDeadlineMinute = workOrder.startMinute + workOrder.durationMinutes + workOrder.replyWindowMinutes;
      const phase = getWorkOrderPhase(workOrder, currentMinutesOfDay);

      return {
        id: `manual-work-order-${workOrder.id}-${formatCompactLocalTime(scheduledAtDate)}`,
        kind: "work_order" as const,
        code: `WO-${formatCompactLocalTime(scheduledAtDate)}-${workOrder.id.toUpperCase().slice(0, 4)}`,
        scheduledAt: formatLocalIsoWithOffset(scheduledAtDate),
        title: workOrder.title,
        summary: workOrder.summary,
        status: getWorkOrderStatus(phase),
        riskLevel: workOrder.riskLevel,
        updatedAt: now.getTime(),
        detail: workOrder.detail,
        recommendation: workOrder.recommendation,
        durationMinutes: workOrder.durationMinutes,
        source: workOrder.source,
        location: workOrder.location,
        dispatcher: workOrder.dispatcher,
        assignee: workOrder.assignee,
        priority: workOrder.priority,
        replyWindowMinutes: workOrder.replyWindowMinutes,
        stages: createWorkOrderStages(workOrder, now, scheduledAtDate, phase),
        replyRequirements:
          currentMinutesOfDay > replyDeadlineMinute
            ? []
            : workOrder.replyRequirements
      };
    })
    .sort((a, b) => Date.parse(a.scheduledAt) - Date.parse(b.scheduledAt));
}

function createMockWorkOrders(currentMinutesOfDay: number): ManualWorkOrderDefinition[] {
  return MOCK_WORK_ORDER_TEMPLATES.map((template) => {
    const startMinute = getMockWorkOrderStartMinute(template, currentMinutesOfDay);

    return {
      ...template,
      startMinute
    };
  });
}

function getMockWorkOrderStartMinute(template: MockWorkOrderTemplate, currentMinutesOfDay: number) {
  if (template.stage === "dispatch") {
    return Math.min(currentMinutesOfDay + 30, 24 * 60 - 1);
  }

  if (template.stage === "execution") {
    return Math.max(0, currentMinutesOfDay - Math.min(20, template.durationMinutes - 5));
  }

  return currentMinutesOfDay - template.durationMinutes - Math.min(10, template.replyWindowMinutes - 5);
}

function getWorkOrderPhase(
  workOrder: ManualWorkOrderDefinition,
  currentMinutesOfDay: number
): WorkOrderPhase {
  if (currentMinutesOfDay < workOrder.startMinute) {
    return "dispatch";
  }

  if (currentMinutesOfDay < workOrder.startMinute + workOrder.durationMinutes) {
    return "execution";
  }

  return "reply";
}

function getWorkOrderStatus(phase: WorkOrderPhase): ScheduledWorkOrderStatus {
  if (phase === "dispatch") {
    return "pending";
  }

  if (phase === "execution") {
    return "running";
  }

  return "awaiting_reply";
}

function createWorkOrderStages(
  workOrder: ManualWorkOrderDefinition,
  now: Date,
  scheduledAtDate: Date,
  phase: WorkOrderPhase
) {
  const dispatchAt = setMinutesOfDay(now, Math.max(0, workOrder.startMinute - workOrder.dispatchLeadMinutes));
  const executionEnd = setMinutesOfDay(now, workOrder.startMinute + workOrder.durationMinutes);
  const replyEnd = setMinutesOfDay(
    now,
    workOrder.startMinute + workOrder.durationMinutes + workOrder.replyWindowMinutes
  );
  return [
    {
      id: "dispatch" as const,
      title: "派发",
      status: phase === "dispatch" ? ("current" as const) : ("done" as const),
      owner: workOrder.dispatcher,
      timeLabel: formatClock(dispatchAt),
      description: `确认${workOrder.location}任务范围、优先级和到场窗口，派发给${workOrder.assignee}。`
    },
    {
      id: "execution" as const,
      title: "执行中",
      status:
        phase === "dispatch"
          ? ("pending" as const)
          : phase === "execution"
            ? ("current" as const)
            : ("done" as const),
      owner: workOrder.assignee,
      timeLabel: formatClockRange(scheduledAtDate, workOrder.durationMinutes),
      description: workOrder.summary
    },
    {
      id: "reply" as const,
      title: "待复令",
      status: phase === "reply" ? ("current" as const) : ("pending" as const),
      owner: `${workOrder.assignee} → 调度中心`,
      timeLabel: formatClockRange(executionEnd, workOrder.replyWindowMinutes),
      description: `提交处置结果、现场照片和影响复核，最迟 ${formatClock(replyEnd)} 前完成复令。`
    }
  ];
}

function getConditionStatus(
  task: ConditionTaskDefinition,
  scheduledAt: Date,
  now: Date,
  signal: TaskSignal
): ScheduledConditionStatus {
  const elapsedMinutes = (now.getTime() - scheduledAt.getTime()) / 60_000;
  const durationMinutes = getTaskExecutionDuration(task);

  if (elapsedMinutes >= 0 && elapsedMinutes < durationMinutes) {
    return "running";
  }

  if (task.id === "smart-dispatch") {
    return "completed";
  }

  if (signal.completeness < 90) {
    return "error";
  }

  if (signal.deviation >= 0.72) {
    return "warning";
  }

  return "completed";
}

function getTaskExecutionDuration(task: ConditionTaskDefinition) {
  if (task.id === GUARANTEED_RUNNING_TASK_ID) {
    return Math.max(task.durationMinutes, task.intervalMinutes);
  }

  return task.durationMinutes;
}

function getRiskLevel(status: ScheduledConditionStatus, signal: TaskSignal): ScheduledConditionRiskLevel {
  if (signal.taskId === "smart-dispatch" && status === "completed") {
    return "normal";
  }

  if (status === "error" || signal.deviation >= 0.9) {
    return "critical";
  }

  if (status === "warning" || status === "running" || signal.deviation >= 0.62) {
    return "attention";
  }

  return "normal";
}

function createTaskSignal(task: ConditionTaskDefinition, scheduledAt: Date): TaskSignal {
  const seed = hashString(`${task.id}-${formatCompactLocalTime(scheduledAt)}`);
  const targetIndex = (seed % 7) + 1;
  const deviation = ((seed >> 3) % 100) / 100;
  const completenessBase = 96 - ((seed >> 5) % 7);

  return {
    completeness: deviation > 0.94 ? 88 : completenessBase,
    deviation,
    target: getSignalTarget(task.id, targetIndex),
    sampleCount: task.id === "scada-diagnosis" ? 96 + (seed % 56) : 24 + (seed % 18),
    taskId: task.id
  };
}

function getSignalTarget(taskId: ConditionTaskId, index: number) {
  if (taskId === "scada-diagnosis") {
    return `SCADA 诊断域-${index}`;
  }

  if (taskId === "smart-dispatch") {
    return `调度指令组-${index}`;
  }

  if (taskId === "network-simulation") {
    return `全网模型边界-${index}`;
  }

  return `${index}# 泵站`;
}

function createDetail(
  task: ConditionTaskDefinition,
  scheduledAt: Date,
  status: ScheduledConditionStatus,
  signal: TaskSignal
) {
  const timeLabel = formatClock(scheduledAt);

  if (status === "running") {
    return `${timeLabel} 的${task.title}正在执行，正在汇总 ${signal.target} 的实时遥测、模型边界和异常阈值。`;
  }

  if (task.id === "smart-dispatch") {
    const instructions = createDispatchInstructions(signal);
    return `${timeLabel} 的${task.title}已完成，已形成${instructions.map((item) => item.action).join("、")}指令。`;
  }

  if (status === "error") {
    return `${timeLabel} 的${task.title}未完整闭环，${signal.target} 数据完整率为 ${signal.completeness}%，需要先复核数据源。`;
  }

  if (status === "warning") {
    return `${timeLabel} 的${task.title}发现关注偏差，${signal.target} 偏离运行基线 ${(signal.deviation * 100).toFixed(0)}%。`;
  }

  return `${timeLabel} 的${task.title}已完成，${signal.target} 关键指标处于当前调度可解释范围。`;
}

function createEvidence(task: ConditionTaskDefinition, signal: TaskSignal, status: ScheduledConditionStatus) {
  if (task.id === "smart-dispatch" && status !== "running") {
    const instructions = createDispatchInstructions(signal);
    return [
      `${task.title}按 ${task.intervalMinutes} 分钟周期执行，本轮对象为 ${signal.target}。`,
      `已生成 ${instructions.length} 条管网要素调整指令，并标记调度员复核后下发。`,
      ...instructions.map((item, index) => `指令 ${index + 1}：${item.target}，${item.command}`)
    ];
  }

  const statusEvidence =
    status === "error"
      ? "数据源完整率低于闭环阈值，已保留失败节点供人工复核。"
      : status === "warning"
        ? "偏差超过关注阈值，建议下一轮继续观察同一对象趋势。"
        : "未发现需要立即处置的连锁风险。";

  return [
    `${task.title}按 ${task.intervalMinutes} 分钟周期执行，本轮对象为 ${signal.target}。`,
    `已读取 ${signal.sampleCount} 个遥测样本，关键测点完整率 ${signal.completeness}%。`,
    statusEvidence
  ];
}

function createTaskKpis(task: ConditionTaskDefinition, signal: TaskSignal): ScheduledConditionKpi[] {
  if (task.id === "scada-diagnosis") {
    const pressureFreshness = Math.max(82, Math.round(99 - signal.deviation * 12));
    const flowFreshness = Math.max(80, Math.round(98 - signal.deviation * 13));
    const abnormalSamples = Math.round(signal.deviation * 9);
    const consistencyScore = Math.max(75, Math.round(99 - signal.deviation * 20));

    return [
      {
        id: "pressure-scada-freshness",
        label: "压力 SCADA 新鲜度",
        value: pressureFreshness.toString(),
        unit: "%",
        threshold: "关注 < 94%，异常 < 90%",
        status: getInverseMetricRisk(pressureFreshness, 94, 90),
        description: "压力监测点最近 5 分钟内有效回传占比，覆盖 DMA 分区压力巡检范围。"
      },
      {
        id: "flow-scada-freshness",
        label: "流量 SCADA 新鲜度",
        value: flowFreshness.toString(),
        unit: "%",
        threshold: "关注 < 93%，异常 < 88%",
        status: getInverseMetricRisk(flowFreshness, 93, 88),
        description: "流量计最近 5 分钟内有效回传占比，用于识别数据滞后和通信异常。"
      },
      {
        id: "abnormal-scada-samples",
        label: "异常样本数",
        value: abnormalSamples.toString(),
        unit: "个",
        threshold: "关注 >= 5，异常 >= 8",
        status: getMetricRisk(abnormalSamples, 5, 8),
        description: "压力、流量样本中出现突变、越界或跨源不一致的数量。"
      },
      {
        id: "pressure-flow-consistency",
        label: "压流一致性评分",
        value: consistencyScore.toString(),
        unit: "分",
        threshold: "关注 < 86，异常 < 80",
        status: getInverseMetricRisk(consistencyScore, 86, 80),
        description: "压力变化与流量变化是否符合水力逻辑的综合评分。"
      }
    ];
  }

  if (task.id === "smart-dispatch") {
    const instructions = createDispatchInstructions(signal);
    const confidence = Math.max(86, Math.round(98 - signal.deviation * 8));
    const expectedPressureGain = roundToOneDecimal(0.8 + signal.deviation * 2.6);

    return [
      {
        id: "dispatch-command-count",
        label: "调整指令数",
        value: instructions.length.toString(),
        unit: "条",
        threshold: "常规 2-4 条",
        status: "normal",
        description: "本轮生成的管网要素调整指令数量。"
      },
      {
        id: "dispatch-confidence",
        label: "指令置信度",
        value: confidence.toString(),
        unit: "%",
        threshold: "关注 < 85%，异常 < 75%",
        status: getInverseMetricRisk(confidence, 85, 75),
        description: "基于实时工况、SCADA 数据质量和模拟结果的调度指令可信度。"
      },
      {
        id: "expected-pressure-gain",
        label: "预计压力改善",
        value: expectedPressureGain.toString(),
        unit: "m",
        threshold: "常规 0.5-3.5m",
        status: "normal",
        description: "执行调整指令后，目标片区末端压力的预计改善幅度。"
      }
    ];
  }

  if (task.id === "network-simulation") {
    const pressureDeviation = roundToOneDecimal(1.8 + signal.deviation * 10.6);
    const flowDeviation = roundToOneDecimal(2.4 + signal.deviation * 12.2);
    const maxPressureDelta = roundToOneDecimal(0.7 + signal.deviation * 7.8);
    const balanceScore = Math.max(72, Math.round(98 - signal.deviation * 22));

    return [
      {
        id: "pressure-deviation",
        label: "模拟-实测压力偏差",
        value: pressureDeviation.toString(),
        unit: "%",
        baseline: "近 7 日同窗模型偏差",
        threshold: "关注 > 8%，异常 > 12%",
        status: getMetricRisk(pressureDeviation, 8, 12),
        description: "对比在线水力模拟结果与 SCADA 压力监测值的平均相对偏差。"
      },
      {
        id: "flow-deviation",
        label: "模拟-实测流量偏差",
        value: flowDeviation.toString(),
        unit: "%",
        baseline: "近 7 日同窗流量偏差",
        threshold: "关注 > 10%，异常 > 15%",
        status: getMetricRisk(flowDeviation, 10, 15),
        description: "对比模型计算流量与 SCADA 流量计回传值的偏差。"
      },
      {
        id: "max-pressure-delta",
        label: "最大节点压差",
        value: maxPressureDelta.toString(),
        unit: "m",
        threshold: "关注 > 5m，异常 > 8m",
        status: getMetricRisk(maxPressureDelta, 5, 8),
        description: "模拟节点压力与实测折算压力之间的最大差值。"
      },
      {
        id: "hydraulic-balance-score",
        label: "水力平衡评分",
        value: balanceScore.toString(),
        unit: "分",
        threshold: "关注 < 84，异常 < 78",
        status: getInverseMetricRisk(balanceScore, 84, 78),
        description: "综合压力、流量、边界条件一致性后的模型可信评分。"
      }
    ];
  }

  if (task.id === "pump-energy") {
    const energyDeviation = roundToOneDecimal(2 + signal.deviation * 13);
    const unitEnergy = roundToTwoDecimals(0.285 + signal.deviation * 0.095);
    const starts = 1 + Math.round(signal.deviation * 5);

    return [
      {
        id: "energy-deviation",
        label: "单位供水电耗偏差",
        value: energyDeviation.toString(),
        unit: "%",
        threshold: "关注 > 10%，异常 > 15%",
        status: getMetricRisk(energyDeviation, 10, 15),
        description: "本轮单位供水电耗相对同窗基线的偏差。"
      },
      {
        id: "unit-energy",
        label: "单位供水电耗",
        value: unitEnergy.toString(),
        unit: "kWh/m3",
        baseline: "同窗经济运行区间 0.28-0.34",
        threshold: "关注 > 0.35，异常 > 0.38",
        status: getMetricRisk(unitEnergy, 0.35, 0.38),
        description: "按泵站出水量折算后的实时能耗水平。"
      },
      {
        id: "pump-starts",
        label: "泵组启停次数",
        value: starts.toString(),
        unit: "次",
        threshold: "关注 >= 4，异常 >= 6",
        status: getMetricRisk(starts, 4, 6),
        description: "当前窗口内泵组启停频率，用于识别低效联动。"
      }
    ];
  }

  return [createCompletenessKpi(signal)];
}

function createCompletenessKpi(signal: TaskSignal): ScheduledConditionKpi {
  return {
    id: "telemetry-completeness",
    label: "遥测完整率",
    value: signal.completeness.toString(),
    unit: "%",
    threshold: "关注 < 93%，异常 < 90%",
    status: getInverseMetricRisk(signal.completeness, 93, 90),
    description: "本轮参与判断的 SCADA/遥测样本完整程度。"
  };
}

function createDispatchInstructions(signal: TaskSignal): DispatchInstruction[] {
  const templates: DispatchInstruction[][] = [
    [
      {
        action: "泵站目标压力微调",
        target: "2# 泵站出水压力",
        command: "将目标压力从 0.42MPa 调整至 0.44MPa，保持变频泵优先运行",
        verification: "15 分钟后复核 DMA-03 末端压力是否提升 1.5m 以上"
      },
      {
        action: "边界阀开度校正",
        target: "DMA-03 / DMA-05 边界阀 BV-031",
        command: "开度由 38% 调整至 42%，限制单次调整幅度不超过 5%",
        verification: "观察相邻分区压差是否回落至 3m 内"
      },
      {
        action: "末端压力观察",
        target: "华山路末端压力点 P-117",
        command: "将该点加入 15 分钟高频复核队列",
        verification: "若压力低于 0.18MPa 持续两轮，转人工调度确认"
      }
    ],
    [
      {
        action: "泵组组合优化",
        target: "1# 泵站 3# / 4# 泵组",
        command: "保持 3# 泵运行，延后 4# 泵启动 10 分钟，避免低效并联区间",
        verification: "复核单位供水电耗是否低于 0.34kWh/m3"
      },
      {
        action: "高区供水边界收敛",
        target: "高区联络阀 HV-204",
        command: "开度下调 3%，减少高区向中区回流",
        verification: "确认高区末端压力不低于 0.22MPa"
      },
      {
        action: "流量计复核",
        target: "人民路 DN300 流量计 F-088",
        command: "将该流量计设为本轮调度结果校验点",
        verification: "15 分钟内流量波动需低于 6%"
      }
    ],
    [
      {
        action: "DMA 边界切换准备",
        target: "DMA-07 北侧边界阀 BV-072",
        command: "保持现状，预置 45% 开度为备选边界方案",
        verification: "若下一轮 SCADA 诊断正常，可进入调度员确认"
      },
      {
        action: "低压片区补压",
        target: "北辰片区支线 PRV-12",
        command: "出口压力设定值上调 0.01MPa，禁止联动超过 0.03MPa",
        verification: "复核北辰消防栓维修工单影响范围内压力恢复"
      },
      {
        action: "模型边界刷新",
        target: "全网模型边界条件",
        command: "使用最新泵站出水、阀门开度和 SCADA 流量刷新模拟边界",
        verification: "模拟-实测压力偏差需回落至 8% 内"
      }
    ],
    [
      {
        action: "阀门开度回退",
        target: "华山路沿线阀门组 VG-11",
        command: "将昨日临时开度回退 2%，恢复常规供水边界",
        verification: "观察华山路和人民路支线压差不超过 2.5m"
      },
      {
        action: "泵站压力保持",
        target: "3# 泵站出口压力",
        command: "维持 0.40MPa 不变，禁止自动升压策略介入",
        verification: "若末端压力连续两轮低于 0.19MPa 再触发升压建议"
      },
      {
        action: "异常测点隔离",
        target: "SCADA 诊断异常测点组",
        command: "调度计算暂不采用异常测点，使用相邻测点插补边界",
        verification: "等待下一轮 5 分钟 SCADA 数据诊断恢复后解除隔离"
      }
    ]
  ];
  const templateIndex = Math.round(signal.deviation * 100) % templates.length;
  const selected = templates[templateIndex];
  const count = 2 + (Math.round(signal.deviation * 10) % 2);

  return selected.slice(0, count);
}

function createConditionReport(
  task: ConditionTaskDefinition,
  status: ScheduledConditionStatus,
  riskLevel: ScheduledConditionRiskLevel,
  signal: TaskSignal,
  kpis: ScheduledConditionKpi[]
): ScheduledConditionReport {
  const attentionKpis = kpis.filter((kpi) => kpi.status !== "normal");
  const conclusion =
    status === "running"
      ? `本轮${task.title}正在形成报告，已先返回 ${signal.target} 的阶段性关键指标。`
      : attentionKpis.length > 0
        ? `本轮${task.title}发现 ${attentionKpis.length} 项关键指标需要关注，建议结合下一轮趋势复核。`
        : `本轮${task.title}关键指标均处于可解释范围，可维持当前调度方案。`;

  return {
    title: `${task.title}报告`,
    conclusion,
    sections: [
      {
        title: "核心判断",
        items: [
          `${signal.target} 本轮样本数 ${signal.sampleCount}，遥测完整率 ${signal.completeness}%。`,
          attentionKpis.length > 0
            ? `关注指标：${attentionKpis.map((kpi) => kpi.label).join("、")}。`
            : "未发现超过关注阈值的关键指标。",
          riskLevel === "critical" ? "当前风险等级为异常，建议先复核数据源和边界条件。" : "当前风险未达到自动处置阈值。"
        ]
      },
      {
        title: "调度指导",
        items: createGuidanceItems(task, attentionKpis, signal)
      }
    ]
  };
}

function createGuidanceItems(
  task: ConditionTaskDefinition,
  attentionKpis: ScheduledConditionKpi[],
  signal: TaskSignal
) {
  if (task.id === "scada-diagnosis") {
    return [
      "优先处理新鲜度或一致性异常的压力、流量测点，避免后续模拟和调度引用失真数据。",
      "异常样本集中在同一 DMA 时，可直接替代分区压力巡检形成关注对象。",
      "若连续两轮 SCADA 诊断异常，应通知数据维护或现场巡检确认通信链路。"
    ];
  }

  if (task.id === "smart-dispatch") {
    const instructions = createDispatchInstructions(signal);
    return [
      ...instructions.map((item, index) => `指令 ${index + 1}：${item.target}，${item.command}；${item.verification}`),
      attentionKpis.length > 0 ? "执行前需先确认 SCADA 数据诊断关注项，必要时等待下一轮数据质量恢复。" : "本轮指令满足自动建议条件，可由调度员复核后下发执行。"
    ];
  }

  if (task.id === "network-simulation") {
    return [
      "优先核对压力和流量偏差最大的 SCADA 测点，确认模型边界是否滞后。",
      "若连续两轮模拟-实测偏差扩大，应重新校准泵站出水边界和关键阀门开度。",
      "偏差回落前，不建议直接依据模型结果执行大范围阀门或泵站动作。"
    ];
  }

  if (task.id === "pump-energy") {
    return [
      "关注单位供水电耗和启停频率，判断是否存在泵组组合不经济。",
      "若电耗偏差持续升高，建议比较相邻泵组效率曲线并优化启停策略。",
      "调整前需确认供压稳定性，避免节能动作引发末端低压。"
    ];
  }

  return ["保留本轮诊断结果，等待下一轮定时任务复核。"];
}

function getMetricRisk(value: number, attentionThreshold: number, criticalThreshold: number): ScheduledConditionRiskLevel {
  if (value >= criticalThreshold) {
    return "critical";
  }

  if (value >= attentionThreshold) {
    return "attention";
  }

  return "normal";
}

function getInverseMetricRisk(value: number, attentionThreshold: number, criticalThreshold: number): ScheduledConditionRiskLevel {
  if (value <= criticalThreshold) {
    return "critical";
  }

  if (value <= attentionThreshold) {
    return "attention";
  }

  return "normal";
}

function roundToOneDecimal(value: number) {
  return Math.round(value * 10) / 10;
}

function roundToTwoDecimals(value: number) {
  return Math.round(value * 100) / 100;
}

function createRecommendation(
  status: ScheduledConditionStatus,
  riskLevel: ScheduledConditionRiskLevel,
  task: ConditionTaskDefinition,
  signal: TaskSignal
) {
  if (status === "running") {
    return "等待本轮工况完成后再确认是否生成处置建议。";
  }

  if (task.id === "smart-dispatch") {
    const instructions = createDispatchInstructions(signal);
    return `建议复核并下发本轮管网要素调整指令：${instructions.map((item) => `${item.target} ${item.command}`).join("；")}。`;
  }

  if (status === "error") {
    return "先复查遥测源连通性和模型输入边界，再决定是否转人工调度复核。";
  }

  if (status === "warning" || riskLevel === "attention") {
    return `保留${task.title}关注项，下一轮继续比对同一对象趋势并复核阈值。`;
  }

  return "维持当前调度方案，无需新增人工工单。";
}

function createAnalysisInsight(
  task: ConditionTaskDefinition,
  status: ScheduledConditionStatus,
  riskLevel: ScheduledConditionRiskLevel,
  signal: TaskSignal
): ScheduledConditionAnalysisInsight | undefined {
  if (status === "error") {
    return {
      summary: `Agent 判断本轮${task.title}未完成闭环，优先按数据源异常与局部水力异常并行排查。`,
      notes: [
        `${signal.target} 数据完整率为 ${signal.completeness}%，低于自动闭环阈值。`,
        "异常证据已保留，可继续打开 Agent 会话补充上下文。",
        "处置前建议先确认影响分区，避免误触发调度动作。"
      ],
      solutionOptions: [
        {
          id: "source-recheck",
          title: "复核数据源",
          scenario: "遥测超时或采样完整率不足时优先执行。",
          action: "重试关键测点拉取，核对 SCADA 新鲜度，并标记缺失源。",
          tradeoff: "动作最稳妥，但会延后现场处置判断。"
        },
        {
          id: "hydraulic-compare",
          title: "水力模型比对",
          scenario: "压力/流量同时偏离基线，疑似局部水力异常。",
          action: "调用最近基线工况对比异常分区，输出可能影响范围。",
          tradeoff: "可快速缩小范围，但依赖模型参数和边界条件质量。"
        },
        {
          id: "manual-dispatch",
          title: "人工调度复核",
          scenario: "异常持续超过一个调度窗口或伴随用户侧反馈。",
          action: "转入人工复核，准备阀门、泵站和现场巡检联动清单。",
          tradeoff: "处置闭环更完整，但会占用调度员和巡检资源。"
        }
      ]
    };
  }

  if (status === "warning" || riskLevel === "attention") {
    return {
      summary: `Agent 将本轮${task.title}标记为关注项，当前偏差不足以触发自动处置。`,
      notes: [
        `${signal.target} 偏离运行基线 ${(signal.deviation * 100).toFixed(0)}%，建议保留本轮证据。`,
        "重点观察压力/流量偏差是否持续扩大，避免单点噪声误判。",
        "可在 Agent 面板继续追问偏差来源和相关历史会话。"
      ],
      escalationCriteria: [
        "连续两轮工况仍高于关注阈值。",
        "关键测点完整率继续下降或出现多源超时。",
        "同一分区出现用户投诉、低压或水质联动线索。"
      ]
    };
  }

  return undefined;
}

function getMinutesOfDay(date: Date) {
  return date.getHours() * 60 + date.getMinutes();
}

function setMinutesOfDay(baseDate: Date, minutesOfDay: number) {
  const nextDate = new Date(baseDate);
  nextDate.setHours(Math.floor(minutesOfDay / 60), minutesOfDay % 60, 0, 0);
  return nextDate;
}

function ceilToInterval(value: number, interval: number) {
  return Math.ceil(value / interval) * interval;
}

function floorToInterval(value: number, interval: number) {
  return Math.floor(value / interval) * interval;
}

function formatClock(date: Date) {
  return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
}

function formatClockRange(start: Date, durationMinutes: number) {
  const end = new Date(start.getTime() + durationMinutes * 60_000);
  return `${formatClock(start)} - ${formatClock(end)}`;
}

function formatCompactLocalTime(date: Date) {
  const yyyy = date.getFullYear().toString();
  const mm = (date.getMonth() + 1).toString().padStart(2, "0");
  const dd = date.getDate().toString().padStart(2, "0");
  const hh = date.getHours().toString().padStart(2, "0");
  const min = date.getMinutes().toString().padStart(2, "0");
  return `${yyyy}${mm}${dd}${hh}${min}`;
}

function formatLocalIsoWithOffset(date: Date) {
  const yyyy = date.getFullYear().toString();
  const mm = (date.getMonth() + 1).toString().padStart(2, "0");
  const dd = date.getDate().toString().padStart(2, "0");
  const hh = date.getHours().toString().padStart(2, "0");
  const min = date.getMinutes().toString().padStart(2, "0");
  const ss = date.getSeconds().toString().padStart(2, "0");
  const offsetMinutes = -date.getTimezoneOffset();
  const offsetSign = offsetMinutes >= 0 ? "+" : "-";
  const offsetAbs = Math.abs(offsetMinutes);
  const offsetHours = Math.floor(offsetAbs / 60).toString().padStart(2, "0");
  const offsetMins = (offsetAbs % 60).toString().padStart(2, "0");

  return `${yyyy}-${mm}-${dd}T${hh}:${min}:${ss}${offsetSign}${offsetHours}:${offsetMins}`;
}

function hashString(value: string) {
  let hash = 0;
  for (let index = 0; index < value.length; index += 1) {
    hash = (hash * 31 + value.charCodeAt(index)) | 0;
  }

  return Math.abs(hash);
}
