/**
 * 按业务域全链路展示的任务选择器（v2）。
 *
 * 每个业务域一个折叠面板，内部按层分组展示完整链路：
 *   ODS 任务 → DWD 表（该域的） → DWS/INDEX 任务
 *
 * 功能：
 * - 同步检查：工具栏右侧 Badge 指示，点击展示差异 Modal
 * - 全选常用 / 全选 / 反选 / 清空 按钮
 * - DWD 表勾选 = 选择要装载的 DWD 表（正向选择，和 ODS/DWS 一致）
 */

import React, { useEffect, useState, useMemo, useCallback } from "react";
import {
  Collapse, Checkbox, Spin, Alert, Button, Space, Typography,
  Tag, Badge, Modal, Tooltip, Divider,
} from "antd";
import {
  CheckCircleOutlined, WarningOutlined, SyncOutlined, TableOutlined,
} from "@ant-design/icons";
import type { CheckboxChangeEvent } from "antd/es/checkbox";
import { fetchTaskRegistry, fetchDwdTablesRich, checkTaskSync } from "../api/tasks";
import type { DwdTableItem as ApiDwdTableItem, SyncCheckResult } from "../api/tasks";
import type { TaskDefinition, DwdTableItem } from "../types";

const { Text } = Typography;

/* 层排序 / 标签 / 颜色 */
const LAYER_ORDER: Record<string, number> = { ODS: 0, DWD: 1, DWS: 2, INDEX: 3, UTILITY: 4 };
const LAYER_LABELS: Record<string, string> = {
  ODS: "ODS 抽取", DWD: "DWD 装载", DWS: "DWS 汇总", INDEX: "DWS 指数", UTILITY: "工具",
};
const LAYER_COLORS: Record<string, string> = {
  ODS: "blue", DWD: "green", DWS: "orange", INDEX: "purple", UTILITY: "default",
};
/* 域排序 */
const DOMAIN_ORDER: Record<string, number> = {
  助教: 0, 结算: 1, 台桌: 2, 会员: 3, 商品: 4, 团购: 5, 库存: 6, 财务: 7, 指数: 8, 通用: 9, 工具: 10,
};

export interface TaskSelectorProps {
  layers: string[];
  selectedTasks: string[];
  onTasksChange: (tasks: string[]) => void;
  selectedDwdTables?: string[];
  onDwdTablesChange?: (tables: string[]) => void;
}

interface DomainGroup {
  domain: string;
  layerTasks: { layer: string; tasks: TaskDefinition[] }[];
  dwdTables: DwdTableItem[];
}

/** 当 layers 包含 DWD 时，DWD_LOAD_FROM_ODS 由 DWD 表过滤区块隐含，不单独显示 */
const HIDDEN_WHEN_DWD_VISIBLE = new Set(["DWD_LOAD_FROM_ODS"]);

/** 按域 + 层构建分组 */
function buildDomainGroups(
  registry: Record<string, TaskDefinition[]>,
  dwdTableGroups: Record<string, DwdTableItem[]>,
  layers: string[],
): DomainGroup[] {
  const hideDwdTasks = layers.includes("DWD");
  const domainSet = new Set<string>();
  const tasksByDomainLayer = new Map<string, Map<string, TaskDefinition[]>>();

  for (const tasks of Object.values(registry)) {
    for (const t of tasks) {
      if (!layers.includes(t.layer)) continue;
      if (hideDwdTasks && HIDDEN_WHEN_DWD_VISIBLE.has(t.code)) continue;
      domainSet.add(t.domain);
      if (!tasksByDomainLayer.has(t.domain)) tasksByDomainLayer.set(t.domain, new Map());
      const layerMap = tasksByDomainLayer.get(t.domain)!;
      if (!layerMap.has(t.layer)) layerMap.set(t.layer, []);
      layerMap.get(t.layer)!.push(t);
    }
  }

  if (layers.includes("DWD")) {
    for (const domain of Object.keys(dwdTableGroups)) domainSet.add(domain);
  }

  const groups: DomainGroup[] = [];
  for (const domain of domainSet) {
    const layerMap = tasksByDomainLayer.get(domain) ?? new Map<string, TaskDefinition[]>();
    const layerTasks: { layer: string; tasks: TaskDefinition[] }[] = [];
    const sortedLayers = [...layerMap.keys()].sort(
      (a, b) => (LAYER_ORDER[a] ?? 99) - (LAYER_ORDER[b] ?? 99),
    );
    for (const layer of sortedLayers) {
      const tasks = layerMap.get(layer)!;
      tasks.sort((a, b) => (a.is_common === b.is_common ? 0 : a.is_common ? -1 : 1));
      layerTasks.push({ layer, tasks });
    }
    const dwdTables = layers.includes("DWD") ? (dwdTableGroups[domain] ?? []) : [];
    if (layerTasks.length > 0 || dwdTables.length > 0) {
      groups.push({ domain, layerTasks, dwdTables });
    }
  }
  groups.sort((a, b) => (DOMAIN_ORDER[a.domain] ?? 99) - (DOMAIN_ORDER[b.domain] ?? 99));
  return groups;
}

const TaskSelector: React.FC<TaskSelectorProps> = ({
  layers, selectedTasks, onTasksChange,
  selectedDwdTables = [], onDwdTablesChange,
}) => {
  const [registry, setRegistry] = useState<Record<string, TaskDefinition[]>>({});
  const [dwdTableGroups, setDwdTableGroups] = useState<Record<string, DwdTableItem[]>>({});
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [syncResult, setSyncResult] = useState<SyncCheckResult | null>(null);
  const [syncLoading, setSyncLoading] = useState(false);
  const [syncModalOpen, setSyncModalOpen] = useState(false);

  /* 加载数据 */
  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setError(null);
    const promises: Promise<void>[] = [
      fetchTaskRegistry()
        .then((data) => { if (!cancelled) setRegistry(data); })
        .catch((err) => { if (!cancelled) setError(err?.message ?? "获取任务列表失败"); }),
    ];
    if (layers.includes("DWD")) {
      promises.push(
        fetchDwdTablesRich()
          .then((data) => {
            if (cancelled) return;
            const converted: Record<string, DwdTableItem[]> = {};
            for (const [domain, items] of Object.entries(data)) {
              converted[domain] = items.map((item: ApiDwdTableItem) => ({
                table_name: item.table_name, display_name: item.display_name,
                domain: item.domain, ods_source: item.ods_source, is_dimension: item.is_dimension,
              }));
            }
            setDwdTableGroups(converted);
          })
          .catch(() => {}),
      );
    }
    Promise.all(promises).finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [layers]);

  /* 首次加载后自动同步检查 */
  useEffect(() => {
    if (Object.keys(registry).length > 0) handleSyncCheck();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [registry]);

  /* CHANGE [2026-02-19] intent: DWD 表正向勾选，加载后默认全选 */
  useEffect(() => {
    if (!onDwdTablesChange) return;
    const allTables = Object.values(dwdTableGroups).flat().map((t) => t.table_name);
    if (allTables.length > 0 && selectedDwdTables.length === 0) {
      onDwdTablesChange(allTables);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dwdTableGroups]);

  const domainGroups = useMemo(
    () => buildDomainGroups(registry, dwdTableGroups, layers),
    [registry, dwdTableGroups, layers],
  );

  const allVisibleCodes = useMemo(
    () => domainGroups.flatMap((g) => g.layerTasks.flatMap((lt) => lt.tasks.map((t) => t.code))),
    [domainGroups],
  );
  const allCommonCodes = useMemo(
    () => domainGroups.flatMap((g) =>
      g.layerTasks.flatMap((lt) => lt.tasks.filter((t) => t.is_common).map((t) => t.code)),
    ),
    [domainGroups],
  );

  /* 同步检查 */
  const handleSyncCheck = useCallback(async () => {
    setSyncLoading(true);
    try { setSyncResult(await checkTaskSync()); }
    catch { setSyncResult({ in_sync: false, backend_only: [], etl_only: [], error: "检查失败" }); }
    finally { setSyncLoading(false); }
  }, []);

  /* 任务选择 */
  const handleSelectAll = useCallback(() => onTasksChange(allVisibleCodes), [allVisibleCodes, onTasksChange]);
  const handleSelectCommon = useCallback(() => onTasksChange(allCommonCodes), [allCommonCodes, onTasksChange]);
  const handleInvert = useCallback(() => {
    const set = new Set(selectedTasks);
    onTasksChange(allVisibleCodes.filter((c) => !set.has(c)));
  }, [allVisibleCodes, selectedTasks, onTasksChange]);
  const handleClear = useCallback(() => onTasksChange([]), [onTasksChange]);

  const handleDomainToggle = useCallback(
    (group: DomainGroup, checked: boolean) => {
      const codes = new Set(group.layerTasks.flatMap((lt) => lt.tasks.map((t) => t.code)));
      if (checked) {
        const merged = new Set(selectedTasks);
        codes.forEach((c) => merged.add(c));
        onTasksChange([...merged]);
      } else {
        onTasksChange(selectedTasks.filter((c) => !codes.has(c)));
      }
    },
    [selectedTasks, onTasksChange],
  );

  const handleTaskToggle = useCallback(
    (code: string, checked: boolean) => {
      onTasksChange(checked ? [...selectedTasks, code] : selectedTasks.filter((c) => c !== code));
    },
    [selectedTasks, onTasksChange],
  );

  /* DWD 表选择 */
  const handleDwdTableToggle = useCallback(
    (tableName: string, checked: boolean) => {
      if (!onDwdTablesChange) return;
      onDwdTablesChange(checked
        ? [...selectedDwdTables, tableName]
        : selectedDwdTables.filter((t) => t !== tableName));
    },
    [selectedDwdTables, onDwdTablesChange],
  );

  const handleDwdDomainToggle = useCallback(
    (tables: DwdTableItem[], checked: boolean) => {
      if (!onDwdTablesChange) return;
      const names = new Set(tables.map((t) => t.table_name));
      if (checked) {
        const merged = new Set(selectedDwdTables);
        names.forEach((n) => merged.add(n));
        onDwdTablesChange([...merged]);
      } else {
        onDwdTablesChange(selectedDwdTables.filter((t) => !names.has(t)));
      }
    },
    [selectedDwdTables, onDwdTablesChange],
  );

  /* 渲染 */
  if (loading) return <Spin tip="加载任务列表…" />;
  if (error) return <Alert type="error" message="加载失败" description={error} />;
  if (domainGroups.length === 0) return <Text type="secondary">当前 Flow 无可选任务</Text>;

  const selectedCount = selectedTasks.filter((c) => allVisibleCodes.includes(c)).length;
  const showDwdFilter = layers.includes("DWD") && !!onDwdTablesChange;

  /** 渲染某个域下的 DWD 表过滤区块 */
  const renderDwdTableFilter = (dwdTables: DwdTableItem[]) => {
    if (!showDwdFilter || dwdTables.length === 0) return null;
    const domainDwdSelected = selectedDwdTables.filter((t) => dwdTables.some((d) => d.table_name === t));
    return (
      <div style={{
        marginTop: 6, marginLeft: 4, padding: "6px 8px",
        background: "#f6ffed", borderRadius: 4, border: "1px solid #d9f7be",
      }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 4 }}>
          <Space size={4}>
            <TableOutlined style={{ color: "#52c41a", fontSize: 12 }} />
            <Text style={{ fontSize: 12, fontWeight: 500 }}>DWD 装载表</Text>
            <Text type="secondary" style={{ fontSize: 11 }}>
              {`${domainDwdSelected.length}/${dwdTables.length}`}
            </Text>
          </Space>
          <Space size={4}>
            <Button size="small" type="link" style={{ padding: 0, fontSize: 11, height: "auto" }}
              onClick={() => handleDwdDomainToggle(dwdTables, true)}>全选</Button>
            <Button size="small" type="link" style={{ padding: 0, fontSize: 11, height: "auto" }}
              onClick={() => handleDwdDomainToggle(dwdTables, false)}>清空</Button>
          </Space>
        </div>
        {dwdTables.map((dt) => (
          <div key={dt.table_name} style={{ padding: "1px 0" }}>
            <Checkbox
              checked={selectedDwdTables.includes(dt.table_name)}
              onChange={(e) => handleDwdTableToggle(dt.table_name, e.target.checked)}
            >
              <Text style={{ fontSize: 12 }}>{dt.table_name}</Text>
              <Text type="secondary" style={{ marginLeft: 6, fontSize: 11 }}>{dt.display_name}</Text>
              {dt.is_dimension && <Tag color="cyan" style={{ marginLeft: 4, fontSize: 10, lineHeight: "16px" }}>维度</Tag>}
            </Checkbox>
          </div>
        ))}
      </div>
    );
  };

  return (
    <div>
      {/* 工具栏 */}
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
        <Space size={4} wrap>
          <Button size="small" onClick={handleSelectCommon}>全选常用</Button>
          <Button size="small" onClick={handleSelectAll}>全选</Button>
          <Button size="small" onClick={handleInvert}>反选</Button>
          <Button size="small" onClick={handleClear}>清空</Button>
          <Text type="secondary" style={{ marginLeft: 4 }}>已选 {selectedCount} / {allVisibleCodes.length}</Text>
        </Space>
        <Tooltip title="对比后端注册表与 ETL 真实任务列表">
          {syncLoading ? (
            <Button size="small" icon={<SyncOutlined spin />} disabled>检查中…</Button>
          ) : syncResult === null ? (
            <Button size="small" icon={<SyncOutlined />} onClick={handleSyncCheck}>同步检查</Button>
          ) : syncResult.in_sync ? (
            <Button size="small" icon={<CheckCircleOutlined />} style={{ color: "#52c41a", borderColor: "#b7eb8f" }} onClick={handleSyncCheck}>已同步</Button>
          ) : (
            <Badge dot>
              <Button size="small" danger icon={<WarningOutlined />} onClick={() => setSyncModalOpen(true)}>有差异</Button>
            </Badge>
          )}
        </Tooltip>
      </div>

      {/* 域折叠面板 */}
      <Collapse
        defaultActiveKey={domainGroups.filter((g) => g.domain !== "工具" && g.domain !== "通用").map((g) => g.domain)}
        items={domainGroups.map((group) => {
          const domainCodes = group.layerTasks.flatMap((lt) => lt.tasks.map((t) => t.code));
          const domainSelected = selectedTasks.filter((c) => domainCodes.includes(c));
          const allChecked = domainCodes.length > 0 && domainSelected.length === domainCodes.length;
          const indeterminate = domainSelected.length > 0 && !allChecked;

          return {
            key: group.domain,
            label: (
              <span onClick={(e) => e.stopPropagation()}>
                <Checkbox
                  indeterminate={indeterminate} checked={allChecked}
                  onChange={(e: CheckboxChangeEvent) => handleDomainToggle(group, e.target.checked)}
                  style={{ marginRight: 8 }}
                />
                {group.domain}
                <Text type="secondary" style={{ marginLeft: 4 }}>({domainSelected.length}/{domainCodes.length})</Text>
              </span>
            ),
            children: (
              <div>
                {(() => {
                  /* 找到 DWD 表过滤应插入的位置：ODS 之后、DWS/INDEX 之前 */
                  const hasDwdLayer = group.layerTasks.some((lt) => lt.layer === "DWD");
                  const shouldInsertDwd = !hasDwdLayer && group.dwdTables.length > 0 && showDwdFilter;
                  /* 插入点：第一个 DWS/INDEX/UTILITY 层之前，若全是 ODS 则在末尾 */
                  const insertIdx = shouldInsertDwd
                    ? group.layerTasks.findIndex((lt) => (LAYER_ORDER[lt.layer] ?? 99) >= (LAYER_ORDER["DWS"] ?? 2))
                    : -1;
                  const effectiveInsertIdx = shouldInsertDwd && insertIdx === -1 ? group.layerTasks.length : insertIdx;

                  const elements: React.ReactNode[] = [];
                  group.layerTasks.forEach((lt, idx) => {
                    /* 在此位置插入 DWD 表过滤 */
                    if (shouldInsertDwd && idx === effectiveInsertIdx) {
                      elements.push(
                        <div key="__dwd_filter__">
                          {elements.length > 0 && <Divider style={{ margin: "6px 0" }} />}
                          <div style={{ marginBottom: 4 }}>
                            <Tag color="green" style={{ fontSize: 11 }}>DWD 装载</Tag>
                          </div>
                          {renderDwdTableFilter(group.dwdTables)}
                        </div>,
                      );
                    }
                    elements.push(
                      <div key={lt.layer}>
                        {elements.length > 0 && <Divider style={{ margin: "6px 0" }} />}
                        <div style={{ marginBottom: 4 }}>
                          <Tag color={LAYER_COLORS[lt.layer] ?? "default"} style={{ fontSize: 11 }}>
                            {LAYER_LABELS[lt.layer] ?? lt.layer}
                          </Tag>
                        </div>
                        <div style={{ paddingLeft: 4 }}>
                          {lt.tasks.map((t) => (
                            <div key={t.code} style={{ padding: "2px 0" }}>
                              <Checkbox
                                checked={selectedTasks.includes(t.code)}
                                onChange={(e) => handleTaskToggle(t.code, e.target.checked)}
                              >
                                <Text strong style={!t.is_common ? { color: "#999" } : undefined}>{t.code}</Text>
                                <Text type="secondary" style={{ marginLeft: 8 }}>{t.name}</Text>
                                {t.description && (t.layer === "DWS" || t.layer === "INDEX") && (
                                  <Text type="secondary" style={{ marginLeft: 6, fontSize: 10, color: "#8c8c8c" }}>({t.description})</Text>
                                )}
                                {!t.is_common && <Tag color="default" style={{ marginLeft: 6, fontSize: 11 }}>不常用</Tag>}
                              </Checkbox>
                            </div>
                          ))}
                        </div>
                        {/* DWD 表过滤紧跟 DWD 层任务 */}
                        {lt.layer === "DWD" && renderDwdTableFilter(group.dwdTables)}
                      </div>,
                    );
                  });
                  /* 所有层遍历完后，若插入点在末尾 */
                  if (shouldInsertDwd && effectiveInsertIdx >= group.layerTasks.length) {
                    elements.push(
                      <div key="__dwd_filter__">
                        {elements.length > 0 && <Divider style={{ margin: "6px 0" }} />}
                        <div style={{ marginBottom: 4 }}>
                          <Tag color="green" style={{ fontSize: 11 }}>DWD 装载</Tag>
                        </div>
                        {renderDwdTableFilter(group.dwdTables)}
                      </div>,
                    );
                  }
                  return elements;
                })()}
              </div>
            ),
          };
        })}
      />

      {/* 同步差异 Modal */}
      <Modal
        title="任务注册表同步检查"
        open={syncModalOpen}
        onCancel={() => setSyncModalOpen(false)}
        footer={[
          <Button key="refresh" icon={<SyncOutlined />} onClick={() => { handleSyncCheck(); }}>重新检查</Button>,
          <Button key="close" type="primary" onClick={() => setSyncModalOpen(false)}>关闭</Button>,
        ]}
      >
        {syncResult?.error ? (
          <Alert type="error" message="检查出错" description={syncResult.error} />
        ) : (
          <div>
            {syncResult?.backend_only && syncResult.backend_only.length > 0 && (
              <div style={{ marginBottom: 12 }}>
                <Text strong style={{ color: "#faad14" }}>后端有但 ETL 无（{syncResult.backend_only.length}）：</Text>
                <div style={{ marginTop: 4 }}>
                  {syncResult.backend_only.map((code) => (
                    <Tag key={code} color="warning" style={{ marginBottom: 4 }}>{code}</Tag>
                  ))}
                </div>
              </div>
            )}
            {syncResult?.etl_only && syncResult.etl_only.length > 0 && (
              <div>
                <Text strong style={{ color: "#ff4d4f" }}>ETL 有但后端无（{syncResult.etl_only.length}）：</Text>
                <div style={{ marginTop: 4 }}>
                  {syncResult.etl_only.map((code) => (
                    <Tag key={code} color="error" style={{ marginBottom: 4 }}>{code}</Tag>
                  ))}
                </div>
              </div>
            )}
            {syncResult?.in_sync && (
              <Alert type="success" message="后端与 ETL 任务列表完全一致" />
            )}
          </div>
        )}
      </Modal>
    </div>
  );
};

export default TaskSelector;
