// @ts-nocheck
/**
 * 플로우 관리 컨트롤러
 */

import { Request, Response } from "express";
import { FlowDefinitionService } from "../services/flowDefinitionService";
import { FlowStepService } from "../services/flowStepService";
import { FlowConnectionService } from "../services/flowConnectionService";
import { FlowExecutionService } from "../services/flowExecutionService";
import { FlowDataMoveService } from "../services/flowDataMoveService";
import { FlowProcedureService } from "../services/flowProcedureService";
import { auditLogService, getClientIp } from "../services/auditLogService";

export class FlowController {
  private flowDefinitionService: FlowDefinitionService;
  private flowStepService: FlowStepService;
  private flowConnectionService: FlowConnectionService;
  private flowExecutionService: FlowExecutionService;
  private flowDataMoveService: FlowDataMoveService;
  private flowProcedureService: FlowProcedureService;

  constructor() {
    this.flowDefinitionService = new FlowDefinitionService();
    this.flowStepService = new FlowStepService();
    this.flowConnectionService = new FlowConnectionService();
    this.flowExecutionService = new FlowExecutionService();
    this.flowDataMoveService = new FlowDataMoveService();
    this.flowProcedureService = new FlowProcedureService();
  }

  // ==================== 플로우 정의 ====================

  /**
   * 플로우 정의 생성
   */
  createFlowDefinition = async (req: Request, res: Response): Promise<void> => {
    try {
      const { 
        name, 
        description, 
        tableName, 
        dbSourceType, 
        dbConnectionId,
        // REST API 관련 필드
        restApiConnectionId,
        restApiEndpoint,
        restApiJsonPath,
      } = req.body;
      const userId = (req as any).user?.userId || "system";
      const userCompanyCode = (req as any).user?.companyCode;

      

      if (!name) {
        res.status(400).json({
          success: false,
          message: "Name is required",
        });
        return;
      }

      // REST API 또는 다중 연결인 경우 테이블 존재 확인 스킵
      const isRestApi = dbSourceType === "restapi" || dbSourceType === "multi_restapi";
      const isMultiConnection = dbSourceType === "multi_restapi" || dbSourceType === "multi_external_db";
      
      // 테이블 이름이 제공된 경우에만 존재 확인 (REST API 및 다중 연결 제외)
      if (tableName && !isRestApi && !isMultiConnection && !tableName.startsWith("_restapi_") && !tableName.startsWith("_multi_restapi_") && !tableName.startsWith("_multi_external_db_")) {
        const tableExists =
          await this.flowDefinitionService.checkTableExists(tableName);
        if (!tableExists) {
          res.status(400).json({
            success: false,
            message: `Table '${tableName}' does not exist`,
          });
          return;
        }
      }

      const flowDef = await this.flowDefinitionService.create(
        { 
          name, 
          description, 
          tableName, 
          dbSourceType, 
          dbConnectionId,
          restApiConnectionId,
          restApiEndpoint,
          restApiJsonPath,
          restApiConnections: req.body.restApiConnections,
        },
        userId,
        userCompanyCode
      );

      auditLogService.log({
        companyCode: userCompanyCode || "",
        userId: userId || "",
        action: "CREATE",
        resourceType: "FLOW",
        resourceId: String(flowDef?.id || ""),
        resourceName: flowDef?.name || name,
        summary: `플로우 "${flowDef?.name || name}" 생성`,
        changes: { after: { name, tableName } },
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      res.json({
        success: true,
        data: flowDef,
      });
    } catch (error: any) {
      console.error("Error creating flow definition:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to create flow definition",
      });
    }
  };

  /**
   * 플로우 정의 목록 조회
   */
  getFlowDefinitions = async (req: Request, res: Response): Promise<void> => {
    try {
      const { tableName, isActive } = req.query;
      const user = (req as any).user;
      const userCompanyCode = user?.companyCode;

      

      const flows = await this.flowDefinitionService.findAll(
        tableName as string | undefined,
        isActive !== undefined ? isActive === "true" : undefined,
        userCompanyCode
      );

      

      res.json({
        success: true,
        data: flows,
      });
    } catch (error: any) {
      console.error("Error fetching flow definitions:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to fetch flow definitions",
      });
    }
  };

  /**
   * 플로우 정의 상세 조회 (단계 및 연결 포함)
   */
  getFlowDefinitionDetail = async (
    req: Request,
    res: Response
  ): Promise<void> => {
    try {
      const { id } = req.params;
      const flowId = parseInt(id);
      const userCompanyCode = (req as any).user?.companyCode;

      const definition = await this.flowDefinitionService.findById(flowId, userCompanyCode);
      if (!definition) {
        res.status(404).json({
          success: false,
          message: "Flow definition not found",
        });
        return;
      }

      const steps = await this.flowStepService.findByFlowId(flowId);
      const connections = await this.flowConnectionService.findByFlowId(flowId);

      res.json({
        success: true,
        data: {
          definition,
          steps,
          connections,
        },
      });
    } catch (error: any) {
      console.error("Error fetching flow definition detail:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to fetch flow definition detail",
      });
    }
  };

  /**
   * 플로우 정의 수정
   */
  updateFlowDefinition = async (req: Request, res: Response): Promise<void> => {
    try {
      const { id } = req.params;
      const flowId = parseInt(id);
      const { name, description, isActive } = req.body;
      const userCompanyCode = (req as any).user?.companyCode;

      const beforeFlow = await this.flowDefinitionService.findById(flowId);
      const flowDef = await this.flowDefinitionService.update(flowId, {
        name,
        description,
        isActive,
      }, userCompanyCode);

      if (!flowDef) {
        res.status(404).json({
          success: false,
          message: "Flow definition not found",
        });
        return;
      }

      auditLogService.log({
        companyCode: userCompanyCode || "",
        userId: (req as any).user?.userId || "",
        action: "UPDATE",
        resourceType: "FLOW",
        resourceId: String(flowId),
        resourceName: flowDef?.name || name,
        summary: `플로우 "${flowDef?.name || name}" 수정`,
        changes: {
          before: { name: beforeFlow?.name, description: beforeFlow?.description, isActive: beforeFlow?.isActive },
          after: { name, description, isActive },
        },
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      res.json({
        success: true,
        data: flowDef,
      });
    } catch (error: any) {
      console.error("Error updating flow definition:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to update flow definition",
      });
    }
  };

  /**
   * 플로우 정의 삭제
   */
  deleteFlowDefinition = async (req: Request, res: Response): Promise<void> => {
    try {
      const { id } = req.params;
      const flowId = parseInt(id);
      const userCompanyCode = (req as any).user?.companyCode;

      const success = await this.flowDefinitionService.delete(flowId, userCompanyCode);

      if (!success) {
        res.status(404).json({
          success: false,
          message: "Flow definition not found",
        });
        return;
      }

      auditLogService.log({
        companyCode: userCompanyCode || "",
        userId: (req as any).user?.userId || "",
        action: "DELETE",
        resourceType: "FLOW",
        resourceId: String(flowId),
        summary: `플로우(ID:${flowId}) 삭제`,
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      res.json({
        success: true,
        message: "Flow definition deleted successfully",
      });
    } catch (error: any) {
      console.error("Error deleting flow definition:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to delete flow definition",
      });
    }
  };

  // ==================== 플로우 단계 ====================

  /**
   * 플로우 단계 목록 조회
   */
  getFlowSteps = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId } = req.params;
      const flowDefinitionId = parseInt(flowId);

      const steps = await this.flowStepService.findByFlowId(flowDefinitionId);

      res.json({
        success: true,
        data: steps,
      });
      return;
    } catch (error: any) {
      console.error("Error fetching flow steps:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to fetch flow steps",
      });
      return;
    }
  };

  /**
   * 플로우 단계 생성
   */
  createFlowStep = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId } = req.params;
      const flowDefinitionId = parseInt(flowId);
      const userCompanyCode = (req as any).user?.companyCode;
      const {
        stepName,
        stepOrder,
        tableName,
        conditionJson,
        color,
        positionX,
        positionY,
      } = req.body;

      if (!stepName || stepOrder === undefined) {
        res.status(400).json({
          success: false,
          message: "stepName and stepOrder are required",
        });
        return;
      }

      // 플로우 소유권 검증
      const flowDef = await this.flowDefinitionService.findById(flowDefinitionId, userCompanyCode);
      if (!flowDef) {
        res.status(404).json({
          success: false,
          message: "Flow definition not found or access denied",
        });
        return;
      }

      const step = await this.flowStepService.create({
        flowDefinitionId,
        stepName,
        stepOrder,
        tableName,
        conditionJson,
        color,
        positionX,
        positionY,
      });

      auditLogService.log({
        companyCode: userCompanyCode || "",
        userId: (req as any).user?.userId || "",
        action: "CREATE",
        resourceType: "FLOW_STEP",
        resourceId: String(step?.id || ""),
        resourceName: stepName,
        summary: `플로우 스텝 "${stepName}" 생성 (플로우 ID:${flowDefinitionId})`,
        changes: { after: { stepName, tableName, stepOrder } },
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      res.json({
        success: true,
        data: step,
      });
    } catch (error: any) {
      console.error("Error creating flow step:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to create flow step",
      });
    }
  };

  /**
   * 플로우 단계 수정
   */
  updateFlowStep = async (req: Request, res: Response): Promise<void> => {
    try {
      const { stepId } = req.params;
      const id = parseInt(stepId);
      const userCompanyCode = (req as any).user?.companyCode;
      const {
        stepName,
        stepOrder,
        tableName,
        conditionJson,
        color,
        positionX,
        positionY,
        moveType,
        statusColumn,
        statusValue,
        targetTable,
        fieldMappings,
        integrationType,
        integrationConfig,
        displayConfig,
      } = req.body;

      // 스텝 소유권 검증: 스텝이 속한 플로우가 사용자 회사 소유인지 확인
      const existingStep = await this.flowStepService.findById(id);
      if (existingStep) {
        const flowDef = await this.flowDefinitionService.findById(existingStep.flowDefinitionId, userCompanyCode);
        if (!flowDef) {
          res.status(403).json({
            success: false,
            message: "Access denied: flow does not belong to your company",
          });
          return;
        }
      }

      const beforeStep = existingStep;
      const step = await this.flowStepService.update(id, {
        stepName,
        stepOrder,
        tableName,
        conditionJson,
        color,
        positionX,
        positionY,
        moveType,
        statusColumn,
        statusValue,
        targetTable,
        fieldMappings,
        integrationType,
        integrationConfig,
        displayConfig,
      });

      if (!step) {
        res.status(404).json({
          success: false,
          message: "Flow step not found",
        });
        return;
      }

      auditLogService.log({
        companyCode: userCompanyCode || "",
        userId: (req as any).user?.userId || "",
        action: "UPDATE",
        resourceType: "FLOW_STEP",
        resourceId: String(id),
        resourceName: step?.stepName || stepName,
        summary: `플로우 스텝 "${step?.stepName || stepName}" 수정`,
        changes: {
          before: { stepName: beforeStep?.stepName, tableName: beforeStep?.tableName, stepOrder: beforeStep?.stepOrder },
          after: { stepName, tableName, stepOrder },
        },
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      res.json({
        success: true,
        data: step,
      });
    } catch (error: any) {
      console.error("Error updating flow step:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to update flow step",
      });
    }
  };

  /**
   * 플로우 단계 삭제
   */
  deleteFlowStep = async (req: Request, res: Response): Promise<void> => {
    try {
      const { stepId } = req.params;
      const id = parseInt(stepId);
      const userCompanyCode = (req as any).user?.companyCode;

      // 스텝 소유권 검증
      const existingStep = await this.flowStepService.findById(id);
      if (existingStep) {
        const flowDef = await this.flowDefinitionService.findById(existingStep.flowDefinitionId, userCompanyCode);
        if (!flowDef) {
          res.status(403).json({
            success: false,
            message: "Access denied: flow does not belong to your company",
          });
          return;
        }
      }

      const success = await this.flowStepService.delete(id);

      if (!success) {
        res.status(404).json({
          success: false,
          message: "Flow step not found",
        });
        return;
      }

      auditLogService.log({
        companyCode: userCompanyCode || "",
        userId: (req as any).user?.userId || "",
        action: "DELETE",
        resourceType: "FLOW_STEP",
        resourceId: String(id),
        resourceName: existingStep?.stepName,
        summary: `플로우 스텝 "${existingStep?.stepName || id}" 삭제`,
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      res.json({
        success: true,
        message: "Flow step deleted successfully",
      });
    } catch (error: any) {
      console.error("Error deleting flow step:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to delete flow step",
      });
    }
  };

  // ==================== 플로우 연결 ====================

  /**
   * 플로우 연결 목록 조회
   */
  getFlowConnections = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId } = req.params;
      const flowDefinitionId = parseInt(flowId);

      const connections =
        await this.flowConnectionService.findByFlowId(flowDefinitionId);

      res.json({
        success: true,
        data: connections,
      });
      return;
    } catch (error: any) {
      console.error("Error fetching flow connections:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to fetch flow connections",
      });
      return;
    }
  };

  /**
   * 플로우 연결 생성
   */
  createConnection = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowDefinitionId, fromStepId, toStepId, label } = req.body;
      const userCompanyCode = (req as any).user?.companyCode;

      if (!flowDefinitionId || !fromStepId || !toStepId) {
        res.status(400).json({
          success: false,
          message: "flowDefinitionId, fromStepId, and toStepId are required",
        });
        return;
      }

      // 플로우 소유권 검증
      const flowDef = await this.flowDefinitionService.findById(flowDefinitionId, userCompanyCode);
      if (!flowDef) {
        res.status(404).json({
          success: false,
          message: "Flow definition not found or access denied",
        });
        return;
      }

      // fromStepId, toStepId가 해당 flow에 속하는지 검증
      const fromStep = await this.flowStepService.findById(fromStepId);
      const toStep = await this.flowStepService.findById(toStepId);
      if (!fromStep || fromStep.flowDefinitionId !== flowDefinitionId ||
          !toStep || toStep.flowDefinitionId !== flowDefinitionId) {
        res.status(400).json({
          success: false,
          message: "fromStepId and toStepId must belong to the specified flow",
        });
        return;
      }

      const connection = await this.flowConnectionService.create({
        flowDefinitionId,
        fromStepId,
        toStepId,
        label,
      });

      auditLogService.log({
        companyCode: userCompanyCode || "",
        userId: (req as any).user?.userId || "",
        action: "CREATE",
        resourceType: "FLOW",
        resourceId: String(flowDefinitionId),
        resourceName: flowDef?.name || "",
        summary: `플로우 "${flowDef?.name}" 연결 생성 (${fromStep?.stepName} → ${toStep?.stepName})`,
        changes: { after: { fromStepId, toStepId, label } },
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      res.json({
        success: true,
        data: connection,
      });
    } catch (error: any) {
      console.error("Error creating connection:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to create connection",
      });
    }
  };

  /**
   * 플로우 연결 삭제
   */
  deleteConnection = async (req: Request, res: Response): Promise<void> => {
    try {
      const { connectionId } = req.params;
      const id = parseInt(connectionId);
      const userCompanyCode = (req as any).user?.companyCode;

      // 연결 소유권 검증
      const existingConn = await this.flowConnectionService.findById(id);
      if (existingConn) {
        const flowDef = await this.flowDefinitionService.findById(existingConn.flowDefinitionId, userCompanyCode);
        if (!flowDef) {
          res.status(403).json({
            success: false,
            message: "Access denied: flow does not belong to your company",
          });
          return;
        }
      }

      const success = await this.flowConnectionService.delete(id);

      if (!success) {
        res.status(404).json({
          success: false,
          message: "Connection not found",
        });
        return;
      }

      auditLogService.log({
        companyCode: userCompanyCode || "",
        userId: (req as any).user?.userId || "",
        action: "DELETE",
        resourceType: "FLOW",
        resourceId: String(existingConn?.flowDefinitionId || id),
        summary: `플로우 연결 삭제 (ID: ${id})`,
        changes: { before: { connectionId: id } },
        ipAddress: getClientIp(req as any),
        requestPath: req.originalUrl,
      });

      res.json({
        success: true,
        message: "Connection deleted successfully",
      });
    } catch (error: any) {
      console.error("Error deleting connection:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to delete connection",
      });
    }
  };

  // ==================== 플로우 실행 ====================

  /**
   * 단계별 데이터 카운트 조회
   */
  getStepDataCount = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId, stepId } = req.params;

      const count = await this.flowExecutionService.getStepDataCount(
        parseInt(flowId),
        parseInt(stepId)
      );

      res.json({
        success: true,
        data: { count },
      });
    } catch (error: any) {
      console.error("Error getting step data count:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to get step data count",
      });
    }
  };

  /**
   * 단계별 데이터 리스트 조회
   */
  getStepDataList = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId, stepId } = req.params;
      const { page = 1, pageSize = 20 } = req.query;

      const data = await this.flowExecutionService.getStepDataList(
        parseInt(flowId),
        parseInt(stepId),
        parseInt(page as string),
        parseInt(pageSize as string)
      );

      res.json({
        success: true,
        data,
      });
    } catch (error: any) {
      console.error("Error getting step data list:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to get step data list",
      });
    }
  };

  /**
   * 플로우 스텝의 컬럼 라벨 조회
   */
  getStepColumnLabels = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId, stepId } = req.params;
      

      const step = await this.flowStepService.findById(parseInt(stepId));
      if (!step) {
        
        res.status(404).json({
          success: false,
          message: "Step not found",
        });
        return;
      }

      const flowDef = await this.flowDefinitionService.findById(
        parseInt(flowId)
      );
      if (!flowDef) {
        
        res.status(404).json({
          success: false,
          message: "Flow definition not found",
        });
        return;
      }

      // 테이블명 결정 (스텝 테이블 우선, 없으면 플로우 테이블)
      const tableName = step.tableName || flowDef.tableName;
      

      if (!tableName) {
        
        res.json({
          success: true,
          data: {},
        });
        return;
      }

      // table_type_columns 테이블에서 라벨 정보 조회
      const { query } = await import("../database/db");
      const labelRows = await query<{
        column_name: string;
        column_label: string | null;
      }>(
        `SELECT column_name, column_label
         FROM table_type_columns
         WHERE table_name = $1 AND column_label IS NOT NULL AND company_code = '*'`,
        [tableName]
      );

      

      // { columnName: label } 형태의 객체로 변환
      const labels: Record<string, string> = {};
      labelRows.forEach((row) => {
        if (row.column_label) {
          labels[row.column_name] = row.column_label;
        }
      });

      

      res.json({
        success: true,
        data: labels,
      });
    } catch (error: any) {
      console.error("❌ [FlowController] 컬럼 라벨 조회 오류:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to get step column labels",
      });
    }
  };

  /**
   * 플로우의 모든 단계별 카운트 조회
   */
  getAllStepCounts = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId } = req.params;

      const counts = await this.flowExecutionService.getAllStepCounts(
        parseInt(flowId)
      );

      res.json({
        success: true,
        data: counts,
      });
    } catch (error: any) {
      console.error("Error getting all step counts:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to get all step counts",
      });
    }
  };

  /**
   * 데이터를 다음 단계로 이동
   */
  moveData = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId, fromStepId, recordId, toStepId, note } = req.body;
      const userId = (req as any).user?.userId || "system";

      if (!flowId || !fromStepId || !recordId || !toStepId) {
        res.status(400).json({
          success: false,
          message: "flowId, fromStepId, recordId, and toStepId are required",
        });
        return;
      }

      await this.flowDataMoveService.moveDataToStep(
        flowId,
        fromStepId,
        toStepId,
        recordId,
        userId,
        note ? { note } : undefined
      );

      res.json({
        success: true,
        message: "Data moved successfully",
      });
    } catch (error: any) {
      console.error("Error moving data:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to move data",
      });
    }
  };

  /**
   * 여러 데이터를 동시에 이동
   */
  moveBatchData = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId, fromStepId, toStepId, dataIds } = req.body;
      const userId = (req as any).user?.userId || "system";

      if (
        !flowId ||
        !fromStepId ||
        !toStepId ||
        !dataIds ||
        !Array.isArray(dataIds)
      ) {
        res.status(400).json({
          success: false,
          message:
            "flowId, fromStepId, toStepId, and dataIds (array) are required",
        });
        return;
      }

      const result = await this.flowDataMoveService.moveBatchData(
        flowId,
        fromStepId,
        toStepId,
        dataIds,
        userId
      );

      const successCount = result.results.filter((r) => r.success).length;
      const failureCount = result.results.filter((r) => !r.success).length;

      res.json({
        success: result.success,
        message: result.success
          ? `${successCount}건의 데이터를 성공적으로 이동했습니다`
          : `${successCount}건 성공, ${failureCount}건 실패`,
        data: {
          successCount,
          failureCount,
          total: dataIds.length,
        },
        results: result.results,
      });
    } catch (error: any) {
      console.error("Error moving batch data:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to move batch data",
      });
    }
  };

  /**
   * 데이터의 플로우 이력 조회
   */
  getAuditLogs = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId, recordId } = req.params;

      const logs = await this.flowDataMoveService.getAuditLogs(
        parseInt(flowId),
        recordId
      );

      res.json({
        success: true,
        data: logs,
      });
    } catch (error: any) {
      console.error("Error getting audit logs:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to get audit logs",
      });
    }
  };

  /**
   * 플로우의 모든 이력 조회
   */
  getFlowAuditLogs = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId } = req.params;
      const { limit = 100 } = req.query;

      const logs = await this.flowDataMoveService.getFlowAuditLogs(
        parseInt(flowId),
        parseInt(limit as string)
      );

      res.json({
        success: true,
        data: logs,
      });
    } catch (error: any) {
      console.error("Error getting flow audit logs:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to get flow audit logs",
      });
    }
  };

  /**
   * 스텝 데이터 업데이트 (인라인 편집)
   */
  updateStepData = async (req: Request, res: Response): Promise<void> => {
    try {
      const { flowId, stepId, recordId } = req.params;
      const updateData = req.body;
      const userId = (req as any).user?.userId || "system";
      const userCompanyCode = (req as any).user?.companyCode;

      if (!flowId || !stepId || !recordId) {
        res.status(400).json({
          success: false,
          message: "flowId, stepId, and recordId are required",
        });
        return;
      }

      if (!updateData || Object.keys(updateData).length === 0) {
        res.status(400).json({
          success: false,
          message: "Update data is required",
        });
        return;
      }

      const result = await this.flowExecutionService.updateStepData(
        parseInt(flowId),
        parseInt(stepId),
        recordId,
        updateData,
        userId,
        userCompanyCode
      );

      res.json({
        success: true,
        message: "Data updated successfully",
        data: result,
      });
    } catch (error: any) {
      console.error("Error updating step data:", error);
      res.status(500).json({
        success: false,
        message: error.message || "Failed to update step data",
      });
    }
  };

  // ==================== 프로시저/함수 ====================

  /**
   * 프로시저/함수 목록 조회
   */
  listProcedures = async (req: Request, res: Response): Promise<void> => {
    try {
      const dbSource = (req.query.dbSource as string) || "internal";
      const connectionId = req.query.connectionId
        ? parseInt(req.query.connectionId as string)
        : undefined;
      const schema = req.query.schema as string | undefined;

      if (dbSource !== "internal" && dbSource !== "external") {
        res.status(400).json({
          success: false,
          message: "dbSource는 internal 또는 external이어야 합니다",
        });
        return;
      }

      if (dbSource === "external" && !connectionId) {
        res.status(400).json({
          success: false,
          message: "외부 DB 조회 시 connectionId가 필요합니다",
        });
        return;
      }

      const procedures = await this.flowProcedureService.listProcedures(
        dbSource,
        connectionId,
        schema
      );

      res.json({ success: true, data: procedures });
    } catch (error: any) {
      console.error("프로시저 목록 조회 실패:", error);
      res.status(500).json({
        success: false,
        message: error.message || "프로시저 목록 조회에 실패했습니다",
      });
    }
  };

  /**
   * 프로시저/함수 파라미터 조회
   */
  getProcedureParameters = async (req: Request, res: Response): Promise<void> => {
    try {
      const { name } = req.params;
      const dbSource = (req.query.dbSource as string) || "internal";
      const connectionId = req.query.connectionId
        ? parseInt(req.query.connectionId as string)
        : undefined;
      const schema = req.query.schema as string | undefined;

      if (!name) {
        res.status(400).json({
          success: false,
          message: "프로시저 이름이 필요합니다",
        });
        return;
      }

      if (dbSource !== "internal" && dbSource !== "external") {
        res.status(400).json({
          success: false,
          message: "dbSource는 internal 또는 external이어야 합니다",
        });
        return;
      }

      const parameters = await this.flowProcedureService.getProcedureParameters(
        name,
        dbSource as "internal" | "external",
        connectionId,
        schema
      );

      res.json({ success: true, data: parameters });
    } catch (error: any) {
      console.error("프로시저 파라미터 조회 실패:", error);
      res.status(500).json({
        success: false,
        message: error.message || "프로시저 파라미터 조회에 실패했습니다",
      });
    }
  };
}
