/**
 * ETL 任务配置页面。
 *
 * 提供 Flow 选择、处理模式、时间窗口、高级选项等配置区域，
 * 以及连接器/Store 选择、任务选择、DWD 表选择、CLI 命令预览和任务提交功能。
 */

import React, { useState, useEffect, useMemo } from "react";
import {
  Card,
  Radio,
  Checkbox,
  InputNumber,
  DatePicker,
  Button,
  Space,
  Typography,
  Input,
  message,
  Row,
  Col,
  Badge,
  Alert,
  TreeSelect,
  Tooltip,
  Segmented,
  Spin,
  Modal,
  Form,
  Select,
  TimePicker,
  Descriptions,
  Tag,
} from "antd";
import {
  SendOutlined,
  ThunderboltOutlined,
  CodeOutlined,
  SettingOutlined,
  ClockCircleOutlined,
  SyncOutlined,
  ShopOutlined,
  ApiOutlined,
  ScheduleOutlined,
} from "@ant-design/icons";
import { useNavigate } from "react-router-dom";
import TaskSelector from "../components/TaskSelector";
import { validateTaskConfig, fetchFlows } from "../api/tasks";
import type { FlowDef, ProcessingModeDef } from "../api/tasks";
import { submitToQueue, executeDirectly, cleanupOutput } from "../api/execution";
import { createSchedule } from "../api/schedules";
import { useAuthStore } from "../store/authStore";
import BusinessDayHint from "../components/BusinessDayHint";
import type { RadioChangeEvent } from "antd";
import type { Dayjs } from "dayjs";
import dayjs from "dayjs";
import type { TaskConfig as TaskConfigType, ScheduleConfig } from "../types";
import type { MinRunIntervalItem } from "../types";

const { Title, Text } = Typography;
const { TextArea } = Input;

/* ------------------------------------------------------------------ */
/*  Flow / 处理模式 — 本地 fallback（API 不可用时兜底）                  */
/* ------------------------------------------------------------------ */

interface FlowEntry { name: string; layers: string[] }

const FALLBACK_FLOWS: Record<string, FlowEntry> = {
  api_ods:       { name: "API → ODS",                      layers: ["ODS"] },
  api_ods_dwd:   { name: "API → ODS → DWD",               layers: ["ODS", "DWD"] },
  api_full:      { name: "API → ODS → DWD → DWS → INDEX", layers: ["ODS", "DWD", "DWS", "INDEX"] },
  ods_dwd:       { name: "ODS → DWD",                      layers: ["DWD"] },
  dwd_dws:       { name: "DWD → DWS汇总",                 layers: ["DWS"] },
  dwd_dws_index: { name: "DWD → DWS → INDEX",             layers: ["DWS", "INDEX"] },
  dwd_index:     { name: "DWD → INDEX",                    layers: ["INDEX"] },
};

interface ProcModeEntry { value: string; label: string; desc: string }

const FALLBACK_PROCESSING_MODES: ProcModeEntry[] = [
  { value: "increment_only",   label: "仅增量",     desc: "按游标增量抓取和装载" },
  { value: "verify_only",      label: "校验并修复", desc: "对比源和目标，修复差异" },
  { value: "increment_verify", label: "增量+校验",  desc: "先增量再校验" },
  { value: "full_window",      label: "全窗口",     desc: "用 API 返回数据的时间范围处理所有层" },
];

/** 将 API 返回的 FlowDef[] 转为 Record<id, FlowEntry> */
function apiFlowsToRecord(flows: FlowDef[]): Record<string, FlowEntry> {
  const result: Record<string, FlowEntry> = {};
  for (const f of flows) result[f.id] = { name: f.name, layers: f.layers };
  return result;
}

/** 将 API 返回的 ProcessingModeDef[] 转为 ProcModeEntry[] */
function apiModesToEntries(modes: ProcessingModeDef[]): ProcModeEntry[] {
  return modes.map((m) => ({ value: m.id, label: m.name, desc: m.description }));
}

/** 外部可用的 getFlowLayers（使用 fallback，组件内部用动态数据） */
export function getFlowLayers(flowId: string): string[] {
  return FALLBACK_FLOWS[flowId]?.layers ?? [];
}

/* ------------------------------------------------------------------ */
/*  时间窗口                                                           */
/* ------------------------------------------------------------------ */

type WindowMode = "lookback" | "custom";

const WINDOW_SPLIT_OPTIONS = [
  { value: 0,  label: "不切分" },
  { value: 1,  label: "1天" },
  { value: 10, label: "10天" },
  { value: 30, label: "30天" },
] as const;

/* ------------------------------------------------------------------ */
/*  连接器 → 门店 树形数据结构                                          */
/* ------------------------------------------------------------------ */

/** 连接器定义：每个连接器下挂载门店列表 */
interface ConnectorDef {
  id: string;
  label: string;
  icon: React.ReactNode;
}

const CONNECTOR_DEFS: ConnectorDef[] = [
  { id: "feiqiu", label: "飞球", icon: <ApiOutlined /> },
];

/** 构建 TreeSelect 的 treeData，连接器为父节点，门店为子节点 */
function buildConnectorStoreTree(
  connectors: ConnectorDef[],
  siteId: number | null,
): { treeData: { title: React.ReactNode; value: string; key: string; children?: { title: React.ReactNode; value: string; key: string }[] }[]; allValues: string[] } {
  const allValues: string[] = [];
  const treeData = connectors.map((c) => {
    // 每个连接器下挂载当前用户的门店（未来可扩展为多门店）
    const stores = siteId
      ? [{ title: (<Space size={4}><ShopOutlined /><span>门店 {siteId}</span></Space>), value: `${c.id}::${siteId}`, key: `${c.id}::${siteId}` }]
      : [];
    stores.forEach((s) => allValues.push(s.value));
    return {
      title: (<Space size={4}>{c.icon}<span>{c.label}</span></Space>),
      value: c.id,
      key: c.id,
      children: stores,
    };
  });
  return { treeData, allValues };
}

/** 从选中值中解析出 store_id 列表 */
function parseSelectedStoreIds(selected: string[]): number[] {
  const ids: number[] = [];
  for (const v of selected) {
    // 格式: "connector::storeId"
    const parts = v.split("::");
    if (parts.length === 2) {
      const num = Number(parts[1]);
      if (!isNaN(num)) ids.push(num);
    }
  }
  return ids;
}

/* ------------------------------------------------------------------ */
/*  页面组件                                                           */
/* ------------------------------------------------------------------ */

const TaskConfig: React.FC = () => {
  const navigate = useNavigate();
  const user = useAuthStore((s) => s.user);

  /* ---------- Flow / 处理模式 动态加载 ---------- */
  const [flowDefs, setFlowDefs] = useState<Record<string, FlowEntry>>(FALLBACK_FLOWS);
  const [procModes, setProcModes] = useState<ProcModeEntry[]>(FALLBACK_PROCESSING_MODES);
  const [flowsLoading, setFlowsLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;
    fetchFlows()
      .then(({ flows, processing_modes }) => {
        if (cancelled) return;
        if (flows.length > 0) setFlowDefs(apiFlowsToRecord(flows));
        if (processing_modes.length > 0) setProcModes(apiModesToEntries(processing_modes));
      })
      .catch(() => { /* API 不可用，使用 fallback */ })
      .finally(() => { if (!cancelled) setFlowsLoading(false); });
    return () => { cancelled = true; };
  }, []);

  /* ---------- 连接器 & Store 树形选择 ---------- */
  const { treeData: connectorTreeData, allValues: allConnectorStoreValues } = useMemo(
    () => buildConnectorStoreTree(CONNECTOR_DEFS, user?.site_id ?? null),
    [user?.site_id],
  );
  // 默认全选
  const [selectedConnectorStores, setSelectedConnectorStores] = useState<string[]>([]);

  // 初始化时默认全选
  useEffect(() => {
    if (selectedConnectorStores.length === 0 && allConnectorStoreValues.length > 0) {
      setSelectedConnectorStores(allConnectorStoreValues);
    }
  }, [allConnectorStoreValues]); // eslint-disable-line react-hooks/exhaustive-deps

  // 从选中值解析 store_id（取第一个，当前单门店场景）
  const selectedStoreIds = useMemo(() => parseSelectedStoreIds(selectedConnectorStores), [selectedConnectorStores]);
  const effectiveStoreId = selectedStoreIds.length === 1 ? selectedStoreIds[0] : null;

  /* ---------- Flow ---------- */
  const [flow, setFlow] = useState<string>("api_ods_dwd");

  /* ---------- 处理模式 ---------- */
  const [processingMode, setProcessingMode] = useState<string>("increment_only");
  const [fetchBeforeVerify, setFetchBeforeVerify] = useState(false);

  /* ---------- 时间窗口 ---------- */
  const [windowMode, setWindowMode] = useState<WindowMode>("lookback");
  const [lookbackHours, setLookbackHours] = useState<number>(24);
  const [overlapSeconds, setOverlapSeconds] = useState<number>(600);
  const [windowStart, setWindowStart] = useState<Dayjs | null>(null);
  const [windowEnd, setWindowEnd] = useState<Dayjs | null>(null);
  const [windowSplitDays, setWindowSplitDays] = useState<number>(0);

  /* ---------- 任务选择 ---------- */
  const [selectedTasks, setSelectedTasks] = useState<string[]>([]);
  const [selectedDwdTables, setSelectedDwdTables] = useState<string[]>([]);
  const [taskIntervals, setTaskIntervals] = useState<Record<string, MinRunIntervalItem>>({});

  /* ---------- 高级选项 ---------- */
  const [dryRun, setDryRun] = useState(false);
  const [forceFull, setForceFull] = useState(false);
  const [useLocalJson, setUseLocalJson] = useState(false);

  /* ---------- Pipeline 调优 ---------- */
  const [pipelineWorkers, setPipelineWorkers] = useState<number | null>(null);
  const [pipelineBatchSize, setPipelineBatchSize] = useState<number | null>(null);
  const [pipelineRateMin, setPipelineRateMin] = useState<number | null>(null);
  const [pipelineRateMax, setPipelineRateMax] = useState<number | null>(null);

  /* ---------- CLI 预览 ---------- */
  const [cliCommand, setCliCommand] = useState<string>("");
  const [cliEdited, setCliEdited] = useState(false);
  const [cliLoading, setCliLoading] = useState(false);

  /* ---------- 提交状态 ---------- */
  const [submitting, setSubmitting] = useState(false);

  /* ---------- 调度任务弹窗 ---------- */
  const [scheduleModalOpen, setScheduleModalOpen] = useState(false);
  const [scheduleSubmitting, setScheduleSubmitting] = useState(false);
  const [scheduleType, setScheduleType] = useState<string>("daily");
  const [scheduleForm] = Form.useForm();

  /* ---------- 派生状态 ---------- */
  const layers = flowDefs[flow]?.layers ?? [];
  const showVerifyOption = processingMode === "verify_only";

  /* ---------- 构建 TaskConfig 对象 ---------- */
  const buildTaskConfig = (): TaskConfigType => {
    /* layers 包含 DWD 时自动注入 DWD_LOAD_FROM_ODS（UI 上由 DWD 表过滤区块隐含） */
    const tasks = layers.includes("DWD") && !selectedTasks.includes("DWD_LOAD_FROM_ODS")
      ? [...selectedTasks, "DWD_LOAD_FROM_ODS"]
      : selectedTasks;
    return {
      tasks,
    flow: flow,
    processing_mode: processingMode,
    pipeline_flow: "FULL",
    dry_run: dryRun,
    window_mode: windowMode,
    window_start: windowMode === "custom" && windowStart ? windowStart.format("YYYY-MM-DD") : null,
    window_end: windowMode === "custom" && windowEnd ? windowEnd.format("YYYY-MM-DD") : null,
    window_split: windowSplitDays > 0 ? "day" : null,
    window_split_days: windowSplitDays > 0 ? windowSplitDays : null,
    lookback_hours: lookbackHours,
    overlap_seconds: overlapSeconds,
    fetch_before_verify: fetchBeforeVerify,
    skip_ods_when_fetch_before_verify: false,
    ods_use_local_json: useLocalJson,
    store_id: effectiveStoreId,
    /* CHANGE [2026-02-19] intent: DWD 表正向勾选，选中=装载 */
    dwd_only_tables: layers.includes("DWD") ? (selectedDwdTables.length > 0 ? selectedDwdTables : null) : null,
    force_full: forceFull,
    pipeline_workers: pipelineWorkers,
    pipeline_batch_size: pipelineBatchSize,
    pipeline_rate_min: pipelineRateMin,
    pipeline_rate_max: pipelineRateMax,
    extra_args: {},
  };
  };

  /* ---------- 自动刷新 CLI 预览 ---------- */
  const refreshCli = async () => {
    setCliLoading(true);
    try {
      const { command } = await validateTaskConfig(buildTaskConfig());
      setCliCommand(command);
      setCliEdited(false);
    } catch {
      // 静默失败，保留上次命令
    } finally {
      setCliLoading(false);
    }
  };

  // 配置变化时自动刷新 CLI（防抖）
  useEffect(() => {
    if (cliEdited) return; // 用户手动编辑过则不自动刷新
    const timer = setTimeout(refreshCli, 500);
    return () => clearTimeout(timer);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [flow, processingMode, fetchBeforeVerify, windowMode, lookbackHours, overlapSeconds,
      windowStart, windowEnd, windowSplitDays, selectedTasks, selectedDwdTables,
      dryRun, forceFull, useLocalJson, selectedConnectorStores,
      pipelineWorkers, pipelineBatchSize, pipelineRateMin, pipelineRateMax]);

  /* ---------- 事件处理 ---------- */
  const handleFlowChange = (e: RadioChangeEvent) => setFlow(e.target.value);

  // CHANGE 2026-03-27 | 包含 ODS 层的 flow 执行前清理输出目录，每类任务只保留最近 10 个运行记录
  const tryCleanupOutput = async () => {
    if (!layers.includes("ODS")) return;
    try {
      const result = await cleanupOutput();
      if (result.dirs_deleted > 0) {
        message.info(`已清理 ${result.dirs_deleted} 个旧运行记录`);
      }
    } catch {
      message.warning("输出目录清理失败，不影响任务执行");
    }
  };

  const handleSubmitToQueue = async () => {
    setSubmitting(true);
    try {
      await tryCleanupOutput();
      await submitToQueue(buildTaskConfig());
      message.success("已提交到执行队列");
      navigate("/etl-tasks?tab=queue");
    } catch (err: unknown) {
      const msg = err instanceof Error ? err.message : "提交失败";
      message.error(`提交到队列失败：${msg}`);
    } finally {
      setSubmitting(false);
    }
  };

  // CHANGE 2026-03-27 | 直接执行后跳转历史 tab 并自动打开任务详情
  const handleExecuteDirectly = async () => {
    setSubmitting(true);
    try {
      await tryCleanupOutput();
      const { execution_id } = await executeDirectly(buildTaskConfig());
      message.success("任务已开始执行");
      navigate(`/etl-tasks?tab=history&openExecution=${execution_id}`);
    } catch (err: unknown) {
      const msg = err instanceof Error ? err.message : "执行失败";
      message.error(`直接执行失败：${msg}`);
    } finally {
      setSubmitting(false);
    }
  };

  /* ---------- 调度任务弹窗 ---------- */
  const handleOpenScheduleModal = () => {
    scheduleForm.resetFields();
    scheduleForm.setFieldsValue({
      schedule_config: {
        schedule_type: "daily",
        interval_value: 1,
        interval_unit: "hours",
        daily_time: dayjs("04:00", "HH:mm"),
        weekly_days: [1],
        weekly_time: dayjs("04:00", "HH:mm"),
        cron_expression: "0 4 * * *",
      },
    });
    setScheduleType("daily");
    setScheduleModalOpen(true);
  };

  const handleScheduleSubmit = async () => {
    try {
      const values = await scheduleForm.validateFields();
      setScheduleSubmitting(true);

      const cfg = { ...values.schedule_config };
      if (cfg.daily_time && typeof cfg.daily_time !== "string") {
        cfg.daily_time = cfg.daily_time.format("HH:mm");
      }
      if (cfg.weekly_time && typeof cfg.weekly_time !== "string") {
        cfg.weekly_time = cfg.weekly_time.format("HH:mm");
      }

      const scheduleConfig: ScheduleConfig = {
        schedule_type: cfg.schedule_type ?? "daily",
        interval_value: cfg.interval_value ?? 1,
        interval_unit: cfg.interval_unit ?? "hours",
        daily_time: cfg.daily_time ?? "04:00",
        weekly_days: cfg.weekly_days ?? [1],
        weekly_time: cfg.weekly_time ?? "04:00",
        cron_expression: cfg.cron_expression ?? "0 4 * * *",
        enabled: true,
        start_date: null,
        end_date: null,
      };

      const taskConfig = buildTaskConfig();
      await createSchedule({
        name: values.name,
        task_codes: taskConfig.tasks,
        task_config: taskConfig,
        schedule_config: scheduleConfig,
        run_immediately: !!values.run_immediately,
        min_run_intervals: Object.keys(taskIntervals).length > 0 ? taskIntervals : undefined,
      });
      message.success("调度任务已创建");
      setScheduleModalOpen(false);
    } catch {
      // 表单验证失败
    } finally {
      setScheduleSubmitting(false);
    }
  };

  /* ---------- 样式常量 ---------- */
  const cardStyle = { marginBottom: 12 };
  const sectionTitleStyle: React.CSSProperties = {
    fontSize: 13, fontWeight: 500, color: "#666", marginBottom: 8, display: "block",
  };

  return (
    <div style={{ maxWidth: 960, margin: "0 auto" }}>
      {/* ---- 页面标题 ---- */}
      <div style={{ marginBottom: 16, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <Title level={4} style={{ margin: 0 }}>
          <SettingOutlined style={{ marginRight: 8 }} />
          任务配置
        </Title>
        <Space>
          <Badge count={selectedTasks.length} size="small" offset={[-4, 0]}>
            <Text type="secondary">已选任务</Text>
          </Badge>
        </Space>
      </div>

      {/* ---- 第一行：连接器/门店 + Flow ---- */}
      <Row gutter={12}>
        <Col span={8}>
          <Card size="small" title={<Space size={4}><ApiOutlined />连接器 / 门店</Space>} style={cardStyle}>
            <TreeSelect
              treeData={connectorTreeData}
              value={selectedConnectorStores}
              onChange={setSelectedConnectorStores}
              treeCheckable
              treeDefaultExpandAll
              showCheckedStrategy={TreeSelect.SHOW_CHILD}
              placeholder="选择连接器和门店"
              style={{ width: "100%" }}
              maxTagCount={3}
              maxTagPlaceholder={(omitted) => `+${omitted.length} 项`}
              treeCheckStrictly={false}
            />
            <Text type="secondary" style={{ fontSize: 11, marginTop: 6, display: "block" }}>
              {selectedStoreIds.length === 0
                ? "未选择门店，将使用 JWT 默认值"
                : `已选 ${selectedStoreIds.length} 个门店`}
            </Text>
          </Card>
        </Col>
        <Col span={16}>
          <Card size="small" title={flowsLoading ? <Space size={4}>执行流程 (Flow) <Spin size="small" /></Space> : "执行流程 (Flow)"} style={cardStyle}>
            <Radio.Group value={flow} onChange={handleFlowChange} style={{ width: "100%" }}>
              <Row gutter={[0, 4]}>
                {Object.entries(flowDefs).map(([id, def]) => (
                  <Col span={12} key={id}>
                    <Tooltip title={def.name}>
                      <Radio value={id}>
                        <Text strong style={{ fontSize: 12 }}>{id}</Text>
                      </Radio>
                    </Tooltip>
                  </Col>
                ))}
              </Row>
            </Radio.Group>
            <div style={{ marginTop: 6, padding: "4px 8px", background: "#f6f8fa", borderRadius: 4 }}>
              <Text type="secondary" style={{ fontSize: 12 }}>
                {layers.join(" → ") || "—"}
              </Text>
            </div>
          </Card>
        </Col>
      </Row>

      {/* ---- 第二行：处理模式 + 时间窗口 ---- */}
      <Row gutter={12}>
        <Col span={8}>
          <Card size="small" title="处理模式" style={cardStyle}>
            <Radio.Group
              value={processingMode}
              onChange={(e) => {
                setProcessingMode(e.target.value);
                if (e.target.value === "increment_only") setFetchBeforeVerify(false);
              }}
            >
              <Space direction="vertical" style={{ width: "100%" }}>
                {procModes.map((m) => (
                  <Radio key={m.value} value={m.value}>
                    <Text strong>{m.label}</Text>
                    <br />
                    <Text type="secondary" style={{ fontSize: 12 }}>{m.desc}</Text>
                  </Radio>
                ))}
              </Space>
            </Radio.Group>
            {showVerifyOption && (
              <Checkbox
                checked={fetchBeforeVerify}
                onChange={(e) => setFetchBeforeVerify(e.target.checked)}
                style={{ marginTop: 8 }}
              >
                校验前从 API 获取
              </Checkbox>
            )}
          </Card>
        </Col>
        <Col span={16}>
          <Card
            size="small"
            title={<><ClockCircleOutlined style={{ marginRight: 6 }} />时间窗口</>}
            style={cardStyle}
          >
            <Row gutter={16}>
              <Col span={24}>
                <Segmented
                  value={windowMode}
                  onChange={(v) => setWindowMode(v as WindowMode)}
                  options={[
                    { value: "lookback", label: "回溯模式" },
                    { value: "custom", label: "自定义范围" },
                  ]}
                  style={{ marginBottom: 12 }}
                />
              </Col>
            </Row>

            {windowMode === "lookback" ? (
              <Row gutter={16}>
                <Col span={12}>
                  <Text style={sectionTitleStyle}>回溯小时数</Text>
                  <InputNumber
                    min={1} max={720} value={lookbackHours}
                    onChange={(v) => setLookbackHours(v ?? 24)}
                    style={{ width: "100%" }}
                    addonAfter="小时"
                  />
                </Col>
                <Col span={12}>
                  <Text style={sectionTitleStyle}>冗余秒数</Text>
                  <InputNumber
                    min={0} max={7200} value={overlapSeconds}
                    onChange={(v) => setOverlapSeconds(v ?? 600)}
                    style={{ width: "100%" }}
                    addonAfter="秒"
                  />
                </Col>
              </Row>
            ) : (
              <>
              <Row gutter={16}>
                <Col span={12}>
                  <Text style={sectionTitleStyle}>开始日期</Text>
                  <DatePicker
                    value={windowStart} onChange={setWindowStart}
                    placeholder="选择开始日期" style={{ width: "100%" }}
                  />
                </Col>
                <Col span={12}>
                  <Text style={sectionTitleStyle}>结束日期</Text>
                  <DatePicker
                    value={windowEnd} onChange={setWindowEnd}
                    placeholder="选择结束日期" style={{ width: "100%" }}
                    status={windowStart && windowEnd && windowEnd.isBefore(windowStart) ? "error" : undefined}
                  />
                </Col>
              </Row>
              <div style={{ marginTop: 4 }}>
                <BusinessDayHint />
              </div>
              </>
            )}

            <div style={{ marginTop: 12 }}>
              <Text style={sectionTitleStyle}>窗口切分</Text>
              <Radio.Group
                value={windowSplitDays}
                onChange={(e) => setWindowSplitDays(e.target.value)}
              >
                {WINDOW_SPLIT_OPTIONS.map((opt) => (
                  <Radio.Button key={opt.value} value={opt.value}>{opt.label}</Radio.Button>
                ))}
              </Radio.Group>
            </div>
          </Card>
        </Col>
      </Row>

      {/* ---- 高级选项（带描述） ---- */}
      <Card size="small" title="高级选项" style={cardStyle}>
        <Row gutter={[24, 8]}>
          <Col span={12}>
            <Checkbox checked={dryRun} onChange={(e) => setDryRun(e.target.checked)}>
              <Text strong>dry-run</Text>
            </Checkbox>
            <div style={{ marginLeft: 24 }}>
              <Text type="secondary" style={{ fontSize: 12 }}>模拟执行，走完整流程但不写入数据库</Text>
            </div>
          </Col>
          <Col span={12}>
            <Checkbox checked={forceFull} onChange={(e) => setForceFull(e.target.checked)}>
              <Text strong>force-full</Text>
            </Checkbox>
            <div style={{ marginLeft: 24 }}>
              <Text type="secondary" style={{ fontSize: 12 }}>强制全量，跳过 hash 去重和变更对比</Text>
            </div>
          </Col>
          <Col span={12}>
            <Checkbox checked={useLocalJson} onChange={(e) => setUseLocalJson(e.target.checked)}>
              <Text strong>本地 JSON</Text>
            </Checkbox>
            <div style={{ marginLeft: 24 }}>
              <Text type="secondary" style={{ fontSize: 12 }}>离线模式，从本地 JSON 回放（等同 --data-source offline）</Text>
            </div>
          </Col>
        </Row>

        {/* Pipeline 调优参数 */}
        <div style={{ marginTop: 12, borderTop: "1px solid #f0f0f0", paddingTop: 12 }}>
          <Text type="secondary" style={{ fontSize: 12, marginBottom: 8, display: "block" }}>
            Pipeline 调优（留空使用默认值）
          </Text>
          <Row gutter={[24, 8]}>
            <Col span={6}>
              <Text style={{ fontSize: 12 }}>并发 workers</Text>
              <InputNumber
                size="small"
                min={1}
                max={32}
                placeholder="默认 3"
                value={pipelineWorkers}
                onChange={(v) => setPipelineWorkers(v)}
                style={{ width: "100%" }}
              />
            </Col>
            <Col span={6}>
              <Text style={{ fontSize: 12 }}>批量大小</Text>
              <InputNumber
                size="small"
                min={1}
                max={10000}
                placeholder="默认 200"
                value={pipelineBatchSize}
                onChange={(v) => setPipelineBatchSize(v)}
                style={{ width: "100%" }}
              />
            </Col>
            <Col span={6}>
              <Text style={{ fontSize: 12 }}>限流下限(秒)</Text>
              <InputNumber
                size="small"
                min={0}
                max={60}
                step={0.1}
                placeholder="默认 1.0"
                value={pipelineRateMin}
                onChange={(v) => setPipelineRateMin(v)}
                style={{ width: "100%" }}
              />
            </Col>
            <Col span={6}>
              <Text style={{ fontSize: 12 }}>限流上限(秒)</Text>
              <InputNumber
                size="small"
                min={0}
                max={60}
                step={0.1}
                placeholder="默认 3.0"
                value={pipelineRateMax}
                onChange={(v) => setPipelineRateMax(v)}
                style={{ width: "100%" }}
              />
            </Col>
          </Row>
        </div>
      </Card>

      {/* ---- 任务选择（含 DWD 表过滤） ---- */}
      <Card size="small" title={<Space size={8}>任务选择<Text type="secondary" style={{ fontSize: 11, fontWeight: 400 }}>右侧可设置每个任务的最小执行间隔</Text></Space>} style={cardStyle}>
        <TaskSelector
          layers={layers}
          selectedTasks={selectedTasks}
          onTasksChange={setSelectedTasks}
          selectedDwdTables={selectedDwdTables}
          onDwdTablesChange={setSelectedDwdTables}
          taskIntervals={taskIntervals}
          onTaskIntervalsChange={setTaskIntervals}
        />
      </Card>

      {/* ---- CLI 命令预览（内嵌可编辑） ---- */}
      <Card
        size="small"
        title={
          <Space>
            <CodeOutlined />
            <span>CLI 命令预览</span>
            {cliEdited && <Text type="warning" style={{ fontSize: 12 }}>（已手动编辑）</Text>}
          </Space>
        }
        extra={
          <Button
            size="small"
            icon={<SyncOutlined spin={cliLoading} />}
            onClick={() => { setCliEdited(false); refreshCli(); }}
          >
            重新生成
          </Button>
        }
        style={cardStyle}
      >
        <TextArea
          value={cliCommand}
          onChange={(e) => { setCliCommand(e.target.value); setCliEdited(true); }}
          autoSize={{ minRows: 2, maxRows: 6 }}
          style={{
            fontFamily: "'Cascadia Code', 'Fira Code', Consolas, monospace",
            fontSize: 13,
            background: "#1e1e1e",
            color: "#d4d4d4",
            border: "none",
            borderRadius: 4,
          }}
          placeholder="配置变更后自动生成 CLI 命令..."
        />
        {cliEdited && (
          <Alert
            type="info"
            showIcon
            message="已手动编辑命令，配置变更不会自动覆盖。点击「重新生成」恢复自动模式。"
            style={{ marginTop: 8 }}
            banner
          />
        )}
      </Card>

      {/* ---- 操作按钮 ---- */}
      <Card size="small" style={{ marginBottom: 24 }}>
        <div style={{ display: "flex", alignItems: "center" }}>
          <Space size="middle">
            <Button
              type="primary"
              size="large"
              icon={<SendOutlined />}
              loading={submitting}
              onClick={handleSubmitToQueue}
            >
              提交到队列
            </Button>
            <Button
              size="large"
              icon={<ScheduleOutlined />}
              onClick={handleOpenScheduleModal}
            >
              添加到调度任务
            </Button>
          </Space>
          <div style={{ flex: 1 }} />
          <Button
            size="large"
            icon={<ThunderboltOutlined />}
            loading={submitting}
            onClick={handleExecuteDirectly}
          >
            直接执行！
          </Button>
        </div>
      </Card>

      {/* ---- 调度任务创建 Modal ---- */}
      <Modal
        title="添加到调度任务"
        open={scheduleModalOpen}
        onOk={handleScheduleSubmit}
        onCancel={() => setScheduleModalOpen(false)}
        confirmLoading={scheduleSubmitting}
        destroyOnClose
        width={560}
      >
        {/* 当前配置摘要（只读） */}
        <Descriptions
          column={1}
          bordered
          size="small"
          style={{ marginBottom: 16 }}
          title="当前任务配置（只读）"
        >
          <Descriptions.Item label="任务">
            {selectedTasks.length > 0
              ? selectedTasks.map((t) => <Tag key={t}>{t}</Tag>)
              : <Text type="secondary">未选择</Text>}
          </Descriptions.Item>
          <Descriptions.Item label="Flow">
            <Tag>{flow}</Tag>
          </Descriptions.Item>
          <Descriptions.Item label="处理模式">{processingMode}</Descriptions.Item>
          <Descriptions.Item label="时间窗口">
            {windowMode === "lookback"
              ? `回溯 ${lookbackHours} 小时`
              : `${windowStart?.format("YYYY-MM-DD") ?? "?"} ~ ${windowEnd?.format("YYYY-MM-DD") ?? "?"}`}
          </Descriptions.Item>
        </Descriptions>

        <Form form={scheduleForm} layout="vertical" preserve={false}>
          <Form.Item
            name="name"
            label="调度任务名称"
            rules={[{ required: true, message: "请输入调度任务名称" }]}
          >
            <Input placeholder="例如：每日全量同步" />
          </Form.Item>

          <Form.Item
            name={["schedule_config", "schedule_type"]}
            label="调度类型"
            rules={[{ required: true }]}
          >
            <Select
              options={[
                { label: "一次性", value: "once" },
                { label: "固定间隔", value: "interval" },
                { label: "每日", value: "daily" },
                { label: "每周", value: "weekly" },
                { label: "Cron", value: "cron" },
              ]}
              onChange={(v: string) => setScheduleType(v)}
            />
          </Form.Item>

          {scheduleType === "interval" && (
            <Space>
              <Form.Item name={["schedule_config", "interval_value"]} noStyle rules={[{ required: true }]}>
                <InputNumber min={1} placeholder="间隔值" />
              </Form.Item>
              <Form.Item name={["schedule_config", "interval_unit"]} noStyle rules={[{ required: true }]}>
                <Select style={{ width: 100 }} options={[
                  { label: "分钟", value: "minutes" },
                  { label: "小时", value: "hours" },
                  { label: "天", value: "days" },
                ]} />
              </Form.Item>
            </Space>
          )}
          {scheduleType === "daily" && (
            <Form.Item name={["schedule_config", "daily_time"]} label="执行时间" rules={[{ required: true }]}>
              <TimePicker format="HH:mm" />
            </Form.Item>
          )}
          {scheduleType === "weekly" && (
            <>
              <Form.Item name={["schedule_config", "weekly_days"]} label="星期" rules={[{ required: true }]}>
                <Checkbox.Group options={[
                  { label: "周一", value: 1 }, { label: "周二", value: 2 },
                  { label: "周三", value: 3 }, { label: "周四", value: 4 },
                  { label: "周五", value: 5 }, { label: "周六", value: 6 },
                  { label: "周日", value: 0 },
                ]} />
              </Form.Item>
              <Form.Item name={["schedule_config", "weekly_time"]} label="执行时间" rules={[{ required: true }]}>
                <TimePicker format="HH:mm" />
              </Form.Item>
            </>
          )}
          {scheduleType === "cron" && (
            <Form.Item name={["schedule_config", "cron_expression"]} label="Cron 表达式" rules={[{ required: true }]}>
              <Input placeholder="0 4 * * *" />
            </Form.Item>
          )}

          <Form.Item name="run_immediately" valuePropName="checked" style={{ marginBottom: 0 }}>
            <Checkbox>创建后立即执行一次</Checkbox>
          </Form.Item>
        </Form>
      </Modal>
    </div>
  );
};

export default TaskConfig;
