import { useEffect, useState, useMemo } from 'react';
import { bitable, FieldType, IDateTimeFieldMeta, IDateTimeField, ITable, ITextField, ISelectionField, INumberField, IOpenSegment, IRecord } from '@lark-base-open/js-sdk';
import { Steps, Button, Typography, Form, Toast, Spin, Empty } from '@douyinfe/semi-ui';
import './App.css';

// 步骤数据结构定义
interface StepItem {
  name: string;
  fieldId: string; // 原始的fieldId，代表流程名称本身
  finished: boolean;
  parallelSteps?: StepItem[]; // 并行子步骤数组
  requireAllSubSteps?: boolean; // 是否所有子步骤都完成才标记主步骤完成
  expectTimeFieldId?: string; 
  expectTimeValue?: number;   
  actualTimeFieldId?: string; 
  actualTimeValue?: number;   
  startTimeFieldId?: string; 
  startTimeValue?: number;   
  processNodeValue?: string;  
  order?: number; // 流程顺序，相同顺序的步骤视为并行
  parallelGroup?: number; // 并行组标识，用于UI分组显示
  isMainStep?: boolean; // 是否为主步骤（整数顺序）
}

// 流程配置项结构
interface ProcessConfigItem {
  processName: string;
  order: number;
  processNodeValue: string;
  timeFieldId: string;
  originalRecordId: string;
  posLink: string;
}

// 常量定义
const POS_TABLE_NAME = "pos_订单基础信息明细"; // 替换为你的实际POS表名
const PROCESS_TABLE_NAME = "订单流程表"; // 替换为你的实际流程配置表名
let POS_TABLE_ID = "";
let PROCESS_TABLE_ID = "";

async function initTableIds() {
  const posTable = await bitable.base.getTableByName(POS_TABLE_NAME);
  POS_TABLE_ID = posTable.id;
  const processTable = await bitable.base.getTableByName(PROCESS_TABLE_NAME);
  PROCESS_TABLE_ID = processTable.id;
}

// 在应用初始化或需要用到表ID前调用
initTableIds();

const PROCESS_NAME_FIELD_NAME = '流程名称'; 
const PROCESS_ORDER_FIELD_NAME = '流程顺序'; 

const POS_LINK_FIELD_NAME_IN_PROCESS_TABLE = 'POS表recordid';
const PROCESS_NODE_FIELD_NAME = '流程节点';
const FIELD_ID_FIELD_NAME = '字段ID';


const PROCESS_NODE_EXPECT_TIME = '预计完成时间'; 
const PROCESS_NODE_ACTUAL_TIME = '实际完成时间'; 
const PROCESS_NODE_START_TIME = '实际开始时间';  

// 格式化时间戳为可读格式
const formatTimestamp = (timestamp: number | undefined): string => {
  if (timestamp === undefined || timestamp === null) return '-';
  return new Date(timestamp).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
};

// 步骤管理服务类
class StepService {
  // 保存步骤完成时间
  static async saveStepCompletionTime(
    posTable: ITable, 
    selectedRecordId: string, 
    stepItem: StepItem, 
    isSubStep: boolean = false
  ): Promise<number | null> {
    if (!selectedRecordId || !stepItem.actualTimeFieldId) return null;
    
    try {
      const timeField = await posTable.getFieldById<IDateTimeField>(stepItem.actualTimeFieldId);
      const now = Date.now();
      
      await timeField.setValue(selectedRecordId, now);
      stepItem.actualTimeValue = now;
      stepItem.finished = true;
      
      console.log(`${isSubStep ? '子步骤' : '步骤'} "${stepItem.name}" 完成时间已保存到字段 ${stepItem.actualTimeFieldId}`);
      return now;
    } catch (e) {
      console.error(`Error saving ${isSubStep ? 'sub-step' : 'step'} completion time:`, e);
      throw e;
    }
  }

  // 更新后续步骤的开始时间
  static async updateSubsequentStepsStartTimes(
    posTable: ITable,
    selectedRecordId: string,
    steps: StepItem[],
    stepIndex: number,
    actualTimeValue: number
  ) {
    if (!selectedRecordId) return;
    
    try {
      // 从当前步骤的下一个步骤开始更新
      for (let i = stepIndex + 1; i < steps.length; i++) {
        const nextStep = steps[i];
        
        // 如果下一步骤已有开始时间，且早于当前完成时间，则不更新
        if (nextStep.startTimeValue && nextStep.startTimeValue <= actualTimeValue) {
          continue;
        }
        
        // 如果下一步骤有开始时间字段ID，更新该字段
        if (nextStep.startTimeFieldId) {
          const startTimeField = await posTable.getFieldById<IDateTimeField>(nextStep.startTimeFieldId);
          await startTimeField.setValue(selectedRecordId, actualTimeValue);
          nextStep.startTimeValue = actualTimeValue;
          console.log(`Updated step ${i} (${nextStep.name}) start time to ${new Date(actualTimeValue).toLocaleString()}`);
        }
        
        // 如果下一步骤有并行子步骤，也更新它们的开始时间
        if (nextStep.parallelSteps) {
          for (const subStep of nextStep.parallelSteps) {
            if (subStep.startTimeFieldId) {
              const subStartTimeField = await posTable.getFieldById<IDateTimeField>(subStep.startTimeFieldId);
              await subStartTimeField.setValue(selectedRecordId, actualTimeValue);
              subStep.startTimeValue = actualTimeValue;
              console.log(`Updated sub-step ${subStep.name} start time to ${new Date(actualTimeValue).toLocaleString()}`);
            }
          }
        }
      }
    } catch (e) {
      console.error("Error updating subsequent steps start times:", e);
      Toast.warning("更新后续步骤开始时间失败");
    }
  }

  // 处理子步骤完成的逻辑
  static async handleSubStepCompletion(
    newSteps: StepItem[],
    stepIndex: number,
    subStepIndex: number,
    posTable: ITable,
    selectedRecordId: string,
    refreshSteps: (value: boolean) => void
  ) {
    const parentStep = newSteps[stepIndex];
    const subStep = parentStep.parallelSteps![subStepIndex];
    
    // 如果父步骤要求所有子步骤完成才标记为完成
    if (parentStep.requireAllSubSteps && parentStep.parallelSteps?.every(ps => ps.finished)) {
      parentStep.finished = true; 
      
      // 如果父步骤也有完成时间字段，记录完成时间
      if (parentStep.actualTimeFieldId) {
        try {
          const completionTime = await StepService.saveStepCompletionTime(posTable, selectedRecordId, parentStep);
          if (completionTime) {
            // 更新父步骤后的步骤开始时间
            await StepService.updateSubsequentStepsStartTimes(
              posTable, selectedRecordId, newSteps, stepIndex, completionTime
            );
            
            // 强制刷新步骤数据
            refreshSteps(true);
          }
        } catch (e) {
          console.error("Error saving parent step completion time:", e);
        }
      } else {
        // 如果没有完成时间字段，也需要更新后续步骤
        refreshSteps(true);
      }
    } else if (!parentStep.requireAllSubSteps && parentStep.parallelSteps?.some(ps => ps.finished)) {
      // 任一子步骤完成即可推进主步骤
      parentStep.finished = true;
      
      // 如果父步骤有完成时间字段，记录完成时间
      if (parentStep.actualTimeFieldId) {
        try {
          const completionTime = await StepService.saveStepCompletionTime(posTable, selectedRecordId, parentStep);
          if (completionTime) {
            // 更新父步骤后的步骤开始时间
            await StepService.updateSubsequentStepsStartTimes(
              posTable, selectedRecordId, newSteps, stepIndex, completionTime
            );
            
            // 强制刷新步骤数据
            refreshSteps(true);
          }
        } catch (e) {
          console.error("Error saving parent step completion time:", e);
        }
      } else {
        // 如果没有完成时间字段，也需要更新后续步骤
        refreshSteps(true);
      }
    }
  }
  
  // 查找上一个主步骤的完成时间
  static findPreviousMainStepFinishTime(steps: StepItem[], currentIndex: number): number | undefined {
    for (let i = currentIndex - 1; i >= 0; i--) {
      const step = steps[i];
      // 如果是主步骤（整数顺序）且已完成
      if (step.isMainStep && step.finished && step.actualTimeValue) {
        return step.actualTimeValue;
      }
    }
    return undefined;
  }
  
  // 更新步骤的开始时间为上一个主步骤的完成时间
  static async updateStepStartTimeFromPreviousMainStep(
    posTable: ITable,
    selectedRecordId: string,
    steps: StepItem[],
    stepIndex: number
  ) {
    if (!selectedRecordId) return;
    
    const currentStep = steps[stepIndex];
    if (!currentStep.startTimeFieldId) return;
    
    try {
      // 查找上一个主步骤的完成时间
      const previousFinishTime = StepService.findPreviousMainStepFinishTime(steps, stepIndex);
      
      if (previousFinishTime) {
        // 更新当前步骤的开始时间
        const startTimeField = await posTable.getFieldById<IDateTimeField>(currentStep.startTimeFieldId);
        await startTimeField.setValue(selectedRecordId, previousFinishTime);
        currentStep.startTimeValue = previousFinishTime;
        console.log(`Updated step ${stepIndex} (${currentStep.name}) start time to previous main step's finish time: ${new Date(previousFinishTime).toLocaleString()}`);
        
        // 如果当前步骤有并行子步骤，也更新它们的开始时间
        if (currentStep.parallelSteps) {
          for (const subStep of currentStep.parallelSteps) {
            if (subStep.startTimeFieldId) {
              const subStartTimeField = await posTable.getFieldById<IDateTimeField>(subStep.startTimeFieldId);
              await subStartTimeField.setValue(selectedRecordId, previousFinishTime);
              subStep.startTimeValue = previousFinishTime;
              console.log(`Updated sub-step ${subStep.name} start time to previous main step's finish time: ${new Date(previousFinishTime).toLocaleString()}`);
            }
          }
        }
      }
    } catch (e) {
      console.error(`Error updating step ${stepIndex} start time from previous main step:`, e);
      Toast.warning("更新步骤开始时间失败");
    }
  }
}

// 判断并行步骤是否完成
const isParallelFinished = (parallelSteps: StepItem[], requireAll: boolean = true) => {
  if (!parallelSteps || parallelSteps.length === 0) return true;
  if (requireAll) {
    return parallelSteps.every(step => step.finished);
  } else {
    return parallelSteps.some(step => step.finished);
  }
};

// 步骤UI组件
const StepCard = ({ 
  step, 
  idx, 
  currentStep, 
  loading, 
  onFinishStep,
  formatTimestamp,
  isCurrentStep,
  isActiveParallelGroup,
  isAnySubStepFinished, // 判断并行组中是否有子步骤已完成
  canAccessSubSteps // 判断是否可以访问子步骤
}: { 
  step: StepItem;
  idx: number;
  currentStep: number;
  loading: boolean;
  onFinishStep: (stepIndex: number, subStepIndex?: number) => void;
  formatTimestamp: (timestamp: number | undefined) => string;
  isCurrentStep: boolean;
  isActiveParallelGroup: boolean;
  isAnySubStepFinished: boolean;
  canAccessSubSteps: boolean;
}) => {
  return (
    <div className="step-content-container" style={{ minWidth: 300, marginTop: step.parallelSteps ? 10 : 0 }}>
      {/* 并行步骤组标题 */}
      {step.parallelSteps && (
        <div className="parallel-group-header" style={{ 
          padding: '10px 16px', 
          backgroundColor: '#f5f7fa', 
          borderRadius: '4px', 
          marginBottom: '16px',
          borderLeft: '4px solid #165DFF'
        }}>
          <Typography.Text strong style={{ fontSize: 16, color: '#165DFF' }}>
            并行处理组: {step.name}
          </Typography.Text>
          <Typography.Text style={{ fontSize: 14, color: '#86909c', marginTop: 4, display: 'block' }}>
            {step.requireAllSubSteps ? '所有子步骤完成后自动推进' : '任一子步骤完成后自动推进'}
          </Typography.Text>
        </div>
      )}

      {/* 普通步骤的时间信息展示 */}
      {!step.parallelSteps && (
        <div className="step-time-info" style={{ 
          margin: '16px 0 8px 0', 
          padding: '12px', 
          border: '1px solid #eee', 
          borderRadius: '4px'
        }}>
          <Typography.Text strong style={{color: '#000', fontSize: 14, marginBottom: 8, display: 'block'}}>
            预计完成时间：{formatTimestamp(step.expectTimeValue)}
          </Typography.Text>
          <Typography.Text strong style={{color: '#000', fontSize: 14, marginBottom: 8, display: 'block'}}>
            实际开始时间：{formatTimestamp(step.startTimeValue)}
          </Typography.Text>
          <Typography.Text strong style={{color: '#000', fontSize: 14, marginBottom: 8, display: 'block'}}>
            实际完成时间：{formatTimestamp(step.actualTimeValue)}
          </Typography.Text>
          <Typography.Text strong style={{
            color: step.actualTimeValue && step.expectTimeValue && step.actualTimeValue > step.expectTimeValue ? '#f5222d' : 
                  (step.actualTimeValue && step.expectTimeValue && step.actualTimeValue <= step.expectTimeValue ? '#52c41a' : '#000'),
            fontSize: 14, marginBottom: 0, display: 'block'
          }}>
            耗时：{step.startTimeValue && step.actualTimeValue ? 
              ((step.actualTimeValue - step.startTimeValue) > 0 ? 
                ((step.actualTimeValue - step.startTimeValue) / 1000 / 60 / 60).toFixed(2) + ' 小时' : '0 小时') : '-'}
          </Typography.Text>
        </div>
      )}

      {/* 并行子步骤 */}
      {step.parallelSteps && (
        <div className="parallel-steps-container" style={{ 
          display: 'flex', 
          flexDirection: 'row', 
          gap: 16, 
          marginTop: 10, 
          flexWrap: 'wrap' 
        }}>
          {step.parallelSteps.map((sub, subIdx) => (
            <div 
              key={subIdx} 
              className="sub-step-card" 
              style={{ 
                flex: 1, 
                minWidth: 280, 
                border: '1px solid #eee', 
                borderRadius: 4, 
                padding: 12,
                backgroundColor: '#fafafa',
                transition: 'all 0.3s',
                position: 'relative'
              }}
            >
              {/* 完成状态标识 */}
              {sub.finished && (
                <div className="sub-step-finished-badge" style={{
                  position: 'absolute',
                  top: '8px',
                  right: '8px',
                  backgroundColor: '#52c41a',
                  color: '#fff',
                  fontSize: '12px',
                  padding: '2px 6px',
                  borderRadius: '4px'
                }}>
                  已完成
                </div>
              )}
              
              <Typography.Text strong className="sub-step-title" style={{ fontSize: 15 }}>{sub.name}</Typography.Text>
              
              <div className="sub-step-time-info" style={{ margin: '10px 0 8px 0' }}>
                <Typography.Text strong style={{color: '#000', fontSize: 13, marginBottom: 8, display: 'block'}}>
                  预计完成时间：{formatTimestamp(sub.expectTimeValue || step.expectTimeValue)}
                </Typography.Text>
                <Typography.Text strong style={{color: '#000', fontSize: 13, marginBottom: 8, display: 'block'}}>
                  实际开始时间：{formatTimestamp(sub.startTimeValue || step.startTimeValue)}
                </Typography.Text>
                <Typography.Text strong style={{color: '#000', fontSize: 13, marginBottom: 8, display: 'block'}}>
                  实际完成时间：{formatTimestamp(sub.actualTimeValue)}
                </Typography.Text>
                <Typography.Text strong style={{
                  color: sub.actualTimeValue && (sub.expectTimeValue || step.expectTimeValue) && 
                       sub.actualTimeValue > (sub.expectTimeValue || step.expectTimeValue) ? '#f5222d' : 
                       (sub.actualTimeValue && (sub.expectTimeValue || step.expectTimeValue) && 
                       sub.actualTimeValue <= (sub.expectTimeValue || step.expectTimeValue) ? '#52c41a' : '#000'),
                  fontSize: 13, marginBottom: 0, display: 'block'
                }}>
                  耗时：{sub.startTimeValue && sub.actualTimeValue ? 
                    ((sub.actualTimeValue - sub.startTimeValue) > 0 ? 
                      ((sub.actualTimeValue - sub.startTimeValue) / 1000 / 60 / 60).toFixed(2) + ' 小时' : '0 小时') : 
                    (step.startTimeValue && sub.actualTimeValue ? 
                      ((sub.actualTimeValue - step.startTimeValue) / 1000 / 60 / 60).toFixed(2) + ' 小时' : '-')}
                </Typography.Text>
              </div>
              
              {/* 允许活动并行组中的子步骤被点击，或并行组中已有子步骤完成 */}
              {!sub.finished && (isCurrentStep || isActiveParallelGroup || isAnySubStepFinished || canAccessSubSteps) && (
                <Button
                  theme="solid"
                  size="small"
                  loading={loading}
                  onClick={() => onFinishStep(idx, subIdx)}
                  style={{ marginTop: 8 }}
                >
                  完成此子步骤
                </Button>
              )}
              
              {/* 只有当父步骤未完成且不在当前活动状态时才显示等待提示 */}
              {!sub.finished && !(isCurrentStep || isActiveParallelGroup || isAnySubStepFinished || canAccessSubSteps) && (
                <div style={{ marginTop: 8, padding: '6px 12px', backgroundColor: '#f2f3f5', borderRadius: 4, textAlign: 'center' }}>
                  <Typography.Text style={{ fontSize: 13, color: '#86909c' }}>
                    等待前置步骤完成
                  </Typography.Text>
                </div>
              )}
            </div>
          ))}
        </div>
      )}

      {/* 当前步骤的完成按钮 */}
      {idx === currentStep && !step.finished && !step.parallelSteps && (
        <Button
          theme="solid"
          size="default"
          loading={loading}
          onClick={() => onFinishStep(idx)}
          style={{ marginTop: 16 }}
        >
          完成此步骤
        </Button>
      )}
    </div>
  );
};

export default function App() {
  const [dateFields, setDateFields] = useState<IDateTimeFieldMeta[]>([]);
  const [steps, setSteps] = useState<StepItem[]>([]);
  const [currentStep, setCurrentStep] = useState(0);
  const [loading, setLoading] = useState(false);
  const [selectedRecordId, setSelectedRecordId] = useState<string | undefined>();
  const [refreshSteps, setRefreshSteps] = useState<boolean>(false);
  const [isChangingRecord, setIsChangingRecord] = useState(false); // 记录是否正在切换记录
  const [hasNoProcess, setHasNoProcess] = useState(false); // 记录是否没有配置流程
  
  // 缓存所有流程配置数据
  const [allProcessConfigs, setAllProcessConfigs] = useState<ProcessConfigItem[]>([]);
  const [processConfigLoading, setProcessConfigLoading] = useState(true);
  // 缓存字段元数据，避免重复获取
  const [fieldMetadataCache, setFieldMetadataCache] = useState<Map<string, any>>(new Map());
  // 缓存按POS记录ID分组的配置
  const [configsByPosLink, setConfigsByPosLink] = useState<Map<string, ProcessConfigItem[]>>(new Map());

  // 预加载所有流程配置数据
  async function fetchAllProcessConfigs() {
    setProcessConfigLoading(true);
    try {
      // 检查缓存中是否已有配置数据
      if (allProcessConfigs.length > 0) {
        console.log('Using cached process configs');
        return;
      }
      
      const processTable = await bitable.base.getTableByName(PROCESS_TABLE_NAME);
      
      // 优化：批量获取所有字段元数据，减少API调用
      const allFields = await processTable.getFieldMetaList();
      const fieldMap = new Map(allFields.map(field => [field.id, field]));
      setFieldMetadataCache(fieldMap);
      
      // 验证字段是否存在 - 使用优化后的字段映射
      const requiredFieldNames = [
        POS_LINK_FIELD_NAME_IN_PROCESS_TABLE,
        PROCESS_NODE_FIELD_NAME,
        FIELD_ID_FIELD_NAME,
        PROCESS_NAME_FIELD_NAME,
        PROCESS_ORDER_FIELD_NAME
      ];
      // 检查必需字段是否存在
      const missingFields: string[] = [];
      requiredFieldNames.forEach(name => {
        const field = allFields.find(f => f.name === name);
        if (!field) {
          missingFields.push(`字段名: ${name}`);
        }
      });
      if (missingFields.length > 0) {
        const errorMsg = `订单流程表中缺少关键字段: ${missingFields.join(', ')}`;
        console.error(errorMsg);
        Toast.error(errorMsg);
        setProcessConfigLoading(false);
        return;
      }
      // 使用更高效的分页获取记录
      const allRecords: IRecord[] = [];
      let pageToken: string | undefined;
      do {
        const response = await processTable.getRecords({
          pageSize: 5000, // 最大页大小
          pageToken
        });
        allRecords.push(...response.records);
        pageToken = response.pageToken;
      } while (pageToken);
      console.log(`Fetched ${allRecords.length} process config records`);
      const allConfigs: ProcessConfigItem[] = [];
      // 获取字段对象
      const posLinkField = allFields.find(f => f.name === POS_LINK_FIELD_NAME_IN_PROCESS_TABLE);
      const processNodeField = allFields.find(f => f.name === PROCESS_NODE_FIELD_NAME);
      const fieldIdField = allFields.find(f => f.name === FIELD_ID_FIELD_NAME);
      for (const record of allRecords) {
        const fields = record.fields;
        const recordId = record.recordId;
        // 处理POS链接字段
        let posLinkText = '';
        const posLinkFieldValue = fields[posLinkField?.id ?? ''];
        if (Array.isArray(posLinkFieldValue)) {
          posLinkText = (posLinkFieldValue[0] as IOpenSegment)?.text?.trim() || '';
        } else if (typeof posLinkFieldValue === 'object' && posLinkFieldValue) {
          posLinkText = (posLinkFieldValue as IOpenSegment).text?.trim() || '';
        }
        // 处理流程名称字段 - 使用字段ID直接访问
        const processNameField = allFields.find(f => f.name === PROCESS_NAME_FIELD_NAME);
        let processName = '';
        if (processNameField) {
          const fieldValue = fields[processNameField.id];
          processName = Array.isArray(fieldValue)
            ? (fieldValue[0] as IOpenSegment)?.text?.trim() || ''
            : (fieldValue as IOpenSegment)?.text?.trim() || '';
        }
        // 处理顺序字段
        const processOrderField = allFields.find(f => f.name === PROCESS_ORDER_FIELD_NAME);
        const order = processOrderField
          ? (fields[processOrderField.id] as number | undefined) || 0
          : 0;
        // 处理流程节点字段
        const processNodeValue = fields[processNodeField?.id ?? '']
          ? (Array.isArray(fields[processNodeField.id])
              ? (fields[processNodeField.id][0] as IOpenSegment)?.text?.trim() || ''
              : (fields[processNodeField.id] as IOpenSegment)?.text?.trim() || '')
          : '';
        // 处理时间字段ID字段
        const timeFieldId = fields[fieldIdField?.id ?? '']
          ? (Array.isArray(fields[fieldIdField.id])
              ? (fields[fieldIdField.id][0] as IOpenSegment)?.text?.trim() || ''
              : (fields[fieldIdField.id] as IOpenSegment)?.text?.trim() || '')
          : '';
        allConfigs.push({
          processName,
          order,
          processNodeValue,
          timeFieldId,
          originalRecordId: recordId,
          posLink: posLinkText
        });
      }
      
      // 按POS记录ID分组配置，便于快速查找
      const configsByPosMap = new Map<string, ProcessConfigItem[]>();
      allConfigs.forEach(config => {
        if (!configsByPosMap.has(config.posLink)) {
          configsByPosMap.set(config.posLink, []);
        }
        configsByPosMap.get(config.posLink)?.push(config);
      });
      
      setAllProcessConfigs(allConfigs);
      setConfigsByPosLink(configsByPosMap);
      console.log('Fetched all process configs:', allConfigs.length, 'items');
    } catch (error) {
      console.error('Failed to fetch all process configs:', error);
      Toast.error('加载流程配置数据失败，请检查网络连接或表格权限');
    } finally {
      setProcessConfigLoading(false);
    }
  }

  useEffect(() => {
    fetchAllProcessConfigs();
  }, []);

  // 初始化日期字段
  useEffect(() => {
    async function fetchDateFieldsForUI() {
      try {
        const activeTable = await bitable.base.getActiveTable();
        const fields = await activeTable.getFieldMetaListByType<IDateTimeFieldMeta>(FieldType.DateTime);
        setDateFields(fields);
      } catch (e) {
        console.error("Failed to fetch date fields for UI: ", e);
        Toast.error("获取可选日期字段列表失败");
      }
    }
    fetchDateFieldsForUI();
  }, []);

  // 优化：初始化步骤时处理并行组
  const initializeStepsWithParallel = (configs: ProcessConfigItem[]) => {
    if (configs.length === 0) return [];
    
    // 使用Map按order分组，提高查询效率
    const stepsByOrder = new Map<number, StepItem[]>();
    
    // 初始化步骤
    configs.forEach(config => {
      const step: StepItem = {
        name: config.processName,
        fieldId: config.timeFieldId,
        finished: false,
        order: config.order,
        processNodeValue: config.processNodeValue,
        isMainStep: config.order === Math.floor(config.order) // 标记是否为主步骤（整数顺序）
      };
      
      if (!stepsByOrder.has(config.order)) {
        stepsByOrder.set(config.order, []);
      }
      stepsByOrder.get(config.order)?.push(step);
    });
    
    // 处理并行组
    const processedSteps: StepItem[] = [];
    stepsByOrder.forEach((groupSteps, order) => {
      if (groupSteps.length === 1) {
        processedSteps.push(groupSteps[0]);
      } else {
        const parallelGroupStep: StepItem = {
          name: `并行组${order}`,
          fieldId: `parallel_group_${order}`,
          finished: false,
          order,
          parallelSteps: groupSteps,
          requireAllSubSteps: true,
          parallelGroup: order,
          isMainStep: true // 并行组作为一个整体视为主步骤
        };
        
        // 继承第一个子步骤的时间字段
        if (groupSteps.length > 0) {
          const firstSubStep = groupSteps[0];
          parallelGroupStep.expectTimeFieldId = firstSubStep.expectTimeFieldId;
          parallelGroupStep.expectTimeValue = firstSubStep.expectTimeValue;
          parallelGroupStep.startTimeFieldId = firstSubStep.startTimeFieldId;
          parallelGroupStep.startTimeValue = firstSubStep.startTimeValue;
        }
        
        processedSteps.push(parallelGroupStep);
      }
    });
    
    return processedSteps.sort((a, b) => (a.order || 0) - (b.order || 0));
  };

  // 刷新步骤数据
  const refreshProcessSteps = async () => {
    if (selectedRecordId) {
      await loadProcessSteps(selectedRecordId);
    }
  };

  // 从缓存中加载流程步骤
  const loadProcessSteps = async (currentPosRecordId: string) => {
    console.log('loadProcessSteps triggered with currentPosRecordId:', currentPosRecordId);
    if (!currentPosRecordId) {
      setSteps([]);
      setCurrentStep(0);
      setHasNoProcess(false);
      console.log('No POS record selected, steps cleared.');
      return;
    }

    setLoading(true);
    setIsChangingRecord(true);
    setHasNoProcess(false); // 重置状态
    console.log('Loading process steps from cached configs...');

    try {
      const posTable = await bitable.base.getTableByName(POS_TABLE_NAME);
      
      // 从缓存中快速筛选当前记录的配置
      const filteredConfigs = configsByPosLink.get(currentPosRecordId) || 
                              allProcessConfigs.filter(config => config.posLink === currentPosRecordId);
      console.log('Filtered configs for current record:', filteredConfigs.length);
      
      if (filteredConfigs.length === 0) {
        console.log('No configs found for current record');
        setSteps([]);
        setCurrentStep(0);
        setHasNoProcess(true); // 设置为没有配置流程
        setLoading(false);
        setIsChangingRecord(false);
        return;
      }

      // 按流程顺序分组配置
      const stepsByOrder = new Map<number, StepItem[]>();
      const tempStepsMap = new Map<string, StepItem>();

      // 首先处理每个步骤的基本信息
        for (const config of filteredConfigs) {
          const stepName = config.processName;
          const order = config.order;
          
          // 如果该步骤不存在，创建新步骤
          if (!tempStepsMap.has(stepName)) {
            tempStepsMap.set(stepName, {
              name: stepName,
              fieldId: stepName,
              finished: false,
              order,
              parallelSteps: undefined,
              requireAllSubSteps: true, // 默认需要所有子步骤完成
              isMainStep: order === Math.floor(order) // 标记是否为主步骤
            });
          } else {
            // 如果步骤已存在，使用最小的order值
            const existingStep = tempStepsMap.get(stepName)!;
            existingStep.order = Math.min(existingStep.order || 0, order);
            // 重新计算是否为主步骤
            existingStep.isMainStep = existingStep.order === Math.floor(existingStep.order);
          }
          
          const stepItem = tempStepsMap.get(stepName)!;
        
        // 根据节点类型设置相应的时间字段
        if (config.timeFieldId) {
          try {
            const posTimeField = await posTable.getFieldById<IDateTimeField>(config.timeFieldId);
            const timeValue = await posTimeField.getValue(currentPosRecordId) as number | null;
            
            if (config.processNodeValue?.includes('预计')) { 
              stepItem.expectTimeFieldId = config.timeFieldId;
              stepItem.expectTimeValue = timeValue || undefined;
            } else if (config.processNodeValue?.includes('实际') && !config.processNodeValue?.includes('开始')) { 
              stepItem.actualTimeFieldId = config.timeFieldId;
              stepItem.actualTimeValue = timeValue || undefined;
              if (timeValue) stepItem.finished = true; 
            } else if (config.processNodeValue === PROCESS_NODE_START_TIME || config.processNodeValue?.includes('实际开始')) { 
              stepItem.startTimeFieldId = config.timeFieldId;
              stepItem.startTimeValue = timeValue || undefined;
            }
          } catch (e) {
            console.warn(`Error processing time field ${config.timeFieldId} for step ${stepName} (node: ${config.processNodeValue}):`, e);
            Toast.warning(`处理步骤 ${stepName} 的时间字段 ${config.timeFieldId} 出错`);
          }
        }
      }

      // 按顺序分组步骤
      const loadedSteps = Array.from(tempStepsMap.values()).sort((a, b) => (a.order || 0) - (b.order || 0));
      
      // 处理并行组 - 相同整数顺序的步骤形成并行组
      const parallelGroups = new Map<number, StepItem[]>();
      loadedSteps.forEach(step => {
        const mainOrder = Math.floor(step.order || 0); // 使用order的整数部分作为分组键
        if (!parallelGroups.has(mainOrder)) {
          parallelGroups.set(mainOrder, []);
        }
        parallelGroups.get(mainOrder)?.push(step);
      });
      
      // 重构步骤数组，将并行组封装为主步骤
      const revisedSteps: StepItem[] = [];
      parallelGroups.forEach((groupSteps, mainOrder) => {
        // 条件：组内多于一个步骤，或者组内只有一个步骤但其原始order包含小数
        const shouldFormParallelBlock = groupSteps.length > 1 || 
                                      (groupSteps.length === 1 && groupSteps[0].order !== Math.floor(groupSteps[0].order || 0));

        if (!shouldFormParallelBlock && groupSteps.length === 1) {
          // 单个普通步骤（order为整数且不成组）直接添加
          revisedSteps.push(groupSteps[0]);
        } else {
          // 形成并行组 (包含单个小数order的步骤，如16.1自己也形成一个"并行组")
          let requiresAll = true; // 默认需要所有子步骤完成
          // 检查组内是否有任何一个步骤的order以.2结尾
          if (groupSteps.some(s => s.order && s.order.toString().endsWith('.2'))) {
            requiresAll = false;
          }

          // 父并行步骤的名称
          // 如果并行块仅由一个原始步骤构成（例如，order为16.1的步骤单独形成一个并行块），则父步骤名称可以就是该原始步骤的名称。
          // 否则，组合所有子步骤名称。
          const parallelStepName = groupSteps.length === 1 
                                   ? groupSteps[0].name 
                                   : `并行处理组 ${mainOrder}`;
          
          revisedSteps.push({
            name: parallelStepName,
            fieldId: `parallel_${mainOrder}`,
            finished: requiresAll 
                        ? groupSteps.every(s => s.finished) 
                        : groupSteps.some(s => s.finished), // 完成状态根据requireAllSubSteps判断
            order: mainOrder, // 父并行步骤使用整数order进行排序
            parallelSteps: groupSteps.sort((a,b) => (a.order || 0) - (b.order || 0)), // 子步骤按原始order排序
            requireAllSubSteps: requiresAll,
            // 尝试从第一个子步骤或具有整数order的子步骤继承时间字段
            expectTimeFieldId: groupSteps.find(s => s.order === mainOrder)?.expectTimeFieldId || groupSteps[0]?.expectTimeFieldId,
            expectTimeValue: groupSteps.find(s => s.order === mainOrder)?.expectTimeValue || groupSteps[0]?.expectTimeValue,
            startTimeFieldId: groupSteps.find(s => s.order === mainOrder)?.startTimeFieldId || groupSteps[0]?.startTimeFieldId,
            startTimeValue: groupSteps.find(s => s.order === mainOrder)?.startTimeValue || groupSteps[0]?.startTimeValue,
            parallelGroup: mainOrder, // 用于UI分组显示的标识
            isMainStep: true // 并行组作为一个整体视为主步骤
          });
        }
      });
      
      // 按顺序排序最终的步骤列表 (并行组和普通步骤)
      const sortedSteps = revisedSteps.sort((a, b) => (a.order || 0) - (b.order || 0));
      
      // 处理步骤间的开始时间关联
      for (let i = 0; i < sortedSteps.length; i++) {
        const currentStep = sortedSteps[i];
        
        // 第一个步骤的开始时间不依赖于前一步骤
        if (i === 0) continue;
        
        // 查找上一个已完成的主步骤（包括并行组）
        let previousMainStepIndex = i - 1;
        while (previousMainStepIndex >= 0 && !sortedSteps[previousMainStepIndex].isMainStep) {
          previousMainStepIndex--;
        }
        
        // 如果找到了上一个主步骤
        if (previousMainStepIndex >= 0) {
          const previousMainStep = sortedSteps[previousMainStepIndex];
          
          // 如果前一个主步骤已完成，且当前步骤没有开始时间，设置当前步骤的开始时间为前一步骤的完成时间
          if (previousMainStep.finished && previousMainStep.actualTimeValue && 
              (!currentStep.startTimeValue || currentStep.startTimeValue < previousMainStep.actualTimeValue)) {
              
            currentStep.startTimeValue = previousMainStep.actualTimeValue;
            
            // 如果当前步骤有开始时间字段ID，也更新该字段的值
            if (currentStep.startTimeFieldId && selectedRecordId) {
              try {
                const startTimeField = await posTable.getFieldById<IDateTimeField>(currentStep.startTimeFieldId);
                await startTimeField.setValue(selectedRecordId, previousMainStep.actualTimeValue);
                console.log(`Set step ${i} (${currentStep.name}) start time to previous main step's finish time: ${new Date(previousMainStep.actualTimeValue).toLocaleString()}`);
              } catch (e) {
                console.warn(`Failed to update start time field for step ${i} (${currentStep.name}):`, e);
              }
            }
          }
        }
      }
      
      console.log('Loaded steps:', JSON.stringify(sortedSteps, null, 2));
      setSteps(sortedSteps);

      // 确定当前步骤
      const firstUnfinished = sortedSteps.findIndex(s => !s.finished);
      setCurrentStep(firstUnfinished === -1 ? sortedSteps.length : firstUnfinished);

    } catch (error) {
      console.error('Failed to load process steps:', error);
      Toast.error('加载流程步骤失败，请检查控制台获取更多信息。');
      setSteps([]);
    } finally {
      setLoading(false);
      setIsChangingRecord(false);
      console.log('Finished loading process steps.');
    }
  };

  // 监听表格选择变化
  useEffect(() => {
    const off = bitable.base.onSelectionChange(async (event) => {
      if (event.data && event.data.tableId === POS_TABLE_ID && event.data.recordId) {
        setSelectedRecordId(event.data.recordId);
        // 只在配置数据加载完成后才加载步骤
        if (!processConfigLoading) {
          await loadProcessSteps(event.data.recordId);
        }
      } else if (!event.data.recordId) {
        setSelectedRecordId(undefined);
        setSteps([]);
        setCurrentStep(0);
        setHasNoProcess(false);
      }
    });
    return () => {
      off();
    };
  }, [POS_TABLE_ID, processConfigLoading]);

  // 监听刷新标志
  useEffect(() => {
    if (refreshSteps && selectedRecordId) {
      refreshProcessSteps();
      setRefreshSteps(false);
    }
  }, [refreshSteps, selectedRecordId]);

  // 处理步骤完成
  const handleFinishStep = async (stepIndex: number, subStepIndex?: number) => {
    if (!selectedRecordId) {
      Toast.error('没有选中的POS记录');
      return;
    }
    
    const newSteps = [...steps];
    let stepToComplete: StepItem;
    let isSubStep = typeof subStepIndex === 'number';

    // 确定要完成的步骤
    if (isSubStep && newSteps[stepIndex] && newSteps[stepIndex].parallelSteps) {
      stepToComplete = newSteps[stepIndex].parallelSteps![subStepIndex!];
    } else {
      stepToComplete = newSteps[stepIndex];
    }

    // 检查步骤是否存在
    if (!stepToComplete) {
      Toast.error('找不到要完成的步骤');
      return;
    }

    // 检查步骤是否已经完成
    if (stepToComplete.finished && stepToComplete.actualTimeValue) {
      Toast.info(`步骤 "${stepToComplete.name}" 已于 ${formatTimestamp(stepToComplete.actualTimeValue)} 完成`);
      return;
    }

    setLoading(true);
    
    try {
      const posTable = await bitable.base.getTableById(POS_TABLE_ID);
      
      // 如果没有配置实际完成时间字段，仅标记为完成
      if (!stepToComplete.actualTimeFieldId) {
        Toast.warning(`步骤 "${stepToComplete.name}" 未配置实际完成时间字段ID，无法自动记录时间。将仅标记为完成。`);
        stepToComplete.finished = true;
      } else {
        // 保存步骤完成时间
        const completionTime = await StepService.saveStepCompletionTime(
          posTable, selectedRecordId, stepToComplete, isSubStep
        );
        Toast.success(`步骤 "${stepToComplete.name}" 时间已记录`);
        
        // 如果是主步骤完成，更新后续步骤的开始时间
        if (!isSubStep && completionTime) {
          await StepService.updateSubsequentStepsStartTimes(
            posTable, selectedRecordId, newSteps, stepIndex, completionTime
          );
          
          // 强制刷新步骤数据
          setRefreshSteps(true);
        }
      }

      // 处理子步骤完成的特殊逻辑
      if (isSubStep) {
        const parentStep = newSteps[stepIndex];
        
        // 如果父步骤要求所有子步骤完成才标记为完成
        if (parentStep.requireAllSubSteps && parentStep.parallelSteps?.every(ps => ps.finished)) {
          parentStep.finished = true; 
          
          // 如果父步骤也有完成时间字段，记录完成时间
          if (parentStep.actualTimeFieldId) {
            try {
              const completionTime = await StepService.saveStepCompletionTime(posTable, selectedRecordId, parentStep);
              if (completionTime) {
                // 更新父步骤后的步骤开始时间
                await StepService.updateSubsequentStepsStartTimes(
                  posTable, selectedRecordId, newSteps, stepIndex, completionTime
                );
                
                // 强制刷新步骤数据
                setRefreshSteps(true);
              }
            } catch (e) {
              console.error("Error saving parent step completion time:", e);
            }
          } else {
            // 如果没有完成时间字段，也需要更新后续步骤
            setRefreshSteps(true);
          }
        } else if (!parentStep.requireAllSubSteps && parentStep.parallelSteps?.some(ps => ps.finished)) {
          // 任一子步骤完成即可推进主步骤
          parentStep.finished = true;
          
          // 如果父步骤有完成时间字段，记录完成时间
          if (parentStep.actualTimeFieldId) {
            try {
              const completionTime = await StepService.saveStepCompletionTime(posTable, selectedRecordId, parentStep);
              if (completionTime) {
                // 更新父步骤后的步骤开始时间
                await StepService.updateSubsequentStepsStartTimes(
                  posTable, selectedRecordId, newSteps, stepIndex, completionTime
                );
                
                // 强制刷新步骤数据
                setRefreshSteps(true);
              }
            } catch (e) {
              console.error("Error saving parent step completion time:", e);
            }
          } else {
            // 如果没有完成时间字段，也需要更新后续步骤
            setRefreshSteps(true);
          }
        }
      }

      // 更新步骤状态和当前步骤
      setSteps(newSteps);
      const firstUnfinished = newSteps.findIndex(s => !s.finished);
      setCurrentStep(firstUnfinished === -1 ? newSteps.length : firstUnfinished);
    } catch (e) {
      console.error("Error finishing step:", e);
      Toast.error(`完成步骤 "${stepToComplete.name}" 失败`);
    } finally {
      setLoading(false);
    }
  };

  // 确定当前并行组是否为活动组
  const isActiveParallelGroup = (step: StepItem): boolean => {
    // 如果当前步骤是并行组，则该并行组是活动的
    if (step.parallelSteps && currentStep === steps.findIndex(s => s === step)) {
      return true;
    }
    
    // 如果当前步骤是并行组中的子步骤，则其父并行组是活动的活动的
    if (currentStep < steps.length && steps[currentStep].parallelSteps) {
      const currentParallelStep = steps[currentStep];
      // 检查当前步骤的并行组ID是否与参数步骤的并行组ID相同
      return currentParallelStep.parallelGroup === step.parallelGroup;
    }
    
    return false;
  };

  // 判断并行组中是否有任何子步骤已完成
  const isAnySubStepFinished = (step: StepItem): boolean => {
    if (!step.parallelSteps) return false;
    return step.parallelSteps.some(subStep => subStep.finished);
  };

  // 判断是否可以访问子步骤
  const canAccessSubSteps = (step: StepItem): boolean => {
    // 如果是当前步骤或活动并行组，当然可以访问
    if (isActiveParallelGroup(step)) return true;
    
    // 如果是已完成的并行组，也可以访问
    if (step.finished) return true;
    
    // 如果并行组中已有任何子步骤完成，也可以访问
    if (isAnySubStepFinished(step)) return true;
    
    // 如果并行组的前置步骤（主步骤）已完成，也可以访问
    const stepIndex = steps.findIndex(s => s === step);
    if (stepIndex > 0) {
      // 查找上一个主步骤
      let previousMainStepIndex = stepIndex - 1;
      while (previousMainStepIndex >= 0 && !steps[previousMainStepIndex].isMainStep) {
        previousMainStepIndex--;
      }
      
      // 如果找到了上一个主步骤且它已完成
      if (previousMainStepIndex >= 0 && steps[previousMainStepIndex].finished) {
        return true;
      }
    }
    
    return false;
  };

  // 渲染加载状态
  if (processConfigLoading) { 
    return (
      <div className="app-container" style={{ padding: 24, minWidth: 410 }}>
        <Typography.Title heading={4}>流程进度</Typography.Title>
        <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '50vh' }}>
          <Spin size="large" tip="加载中..." />
        </div>
      </div>
    );
  }

  // 渲染无流程记录提示
  if (hasNoProcess && selectedRecordId) {
    return (
      <div className="app-container" style={{ padding: 24, minWidth: 410 }}>
        <Typography.Title heading={4}>流程进度</Typography.Title>
        <div style={{ marginTop: 24, textAlign: 'center' }}>
          <Empty
            description={
              <Typography.Text style={{ fontSize: 16, color: '#4e5969' }}>
                此记录没有配置流程
              </Typography.Text>
            }
          />
          <Button
            onClick={async () => { // 修改为 async 函数
              setHasNoProcess(false);
              await fetchAllProcessConfigs(); // 首先重新获取所有流程配置
              if (selectedRecordId) {
                await loadProcessSteps(selectedRecordId); // 然后使用新的配置加载步骤
              }
            }}
            style={{ marginTop: 20 }}
            loading={processConfigLoading || loading} // 当任一加载进行中时显示loading
          >
            刷新重试
          </Button>
        </div>
      </div>
    );
  }

  // 渲染切换记录时的加载状态
  if (isChangingRecord) {
    return (
      <div className="app-container" style={{ padding: 24, minWidth: 410 }}>
        <Typography.Title heading={4}>流程进度</Typography.Title>
        <div style={{ 
          display: 'flex', 
          flexDirection: 'column', 
          justifyContent: 'center', 
          alignItems: 'center', 
          height: '50vh',
          backgroundColor: '#fafafa',
          borderRadius: 8,
          border: '1px solid #e5e6eb'
        }}>
          <Spin 
            size="large" 
            style={{ marginBottom: 20 }}
          />
          <Typography.Title 
            heading={5} 
            style={{ 
              fontSize: 18, 
              fontWeight: 500, 
              color: '#4e5969',
              textAlign: 'center'
            }}
          >
            正在加载流程数据...
          </Typography.Title>
          <Typography.Text 
            style={{ 
              marginTop: 8, 
              color: '#86909c',
              fontSize: 14
            }}
          >
            请稍候，系统正在为您获取当前记录的流程信息
          </Typography.Text>
        </div>
      </div>
    );
  }

  // 渲染主界面
  return (
    <div className="app-container" style={{ padding: 24, minWidth: 410 }}>
      <Typography.Title heading={4}>流程进度</Typography.Title>
      <Steps direction="vertical" type="basic" current={currentStep} style={{ margin: '28px 0' }}>
        {steps.map((step, idx) => (
          <Steps.Step
            key={step.name}
            title={step.name}
            status={step.finished ? 'finish' : idx === currentStep ? 'process' : 'wait'}
            description={
              <StepCard 
                step={step} 
                idx={idx} 
                currentStep={currentStep} 
                loading={loading} 
                onFinishStep={handleFinishStep}
                formatTimestamp={formatTimestamp}
                isCurrentStep={idx === currentStep}
                isActiveParallelGroup={isActiveParallelGroup(step)}
                isAnySubStepFinished={isAnySubStepFinished(step)}
                canAccessSubSteps={canAccessSubSteps(step)}
              />
            }
          />
        ))}
      </Steps>
    </div>
  );
}
