/**
 * 확장된 데이터플로우 제어 서비스
 * 다중 커넥션 지원 및 외부 DB 연동 기능 포함
 */

import {
  DataflowControlService,
  ControlAction,
  ControlCondition,
} from "./dataflowControlService";
import { MultiConnectionQueryService } from "./multiConnectionQueryService";
import { logger } from "../utils/logger";

export interface EnhancedControlAction
  extends Omit<ControlAction, "id" | "conditions" | "fieldMappings"> {
  // 🆕 기본 ControlAction 속성들 (일부 재정의)
  id: string; // ControlAction과 호환성을 위해 string 타입 유지
  fromTable: string;

  // 🆕 추가 속성들 (선택적으로 재정의)
  conditions: ControlCondition[]; // 필수 속성으로 변경
  fieldMappings: any[]; // 필수 속성으로 변경

  // 🆕 UPDATE 액션 관련 필드
  updateConditions?: UpdateCondition[];
  updateFields?: UpdateFieldMapping[];

  // 🆕 DELETE 액션 관련 필드
  deleteConditions?: DeleteCondition[];
  deleteWhereConditions?: DeleteWhereCondition[];
  maxDeleteCount?: number;
  requireConfirmation?: boolean;
  dryRunFirst?: boolean;
  logAllDeletes?: boolean;
}

export interface UpdateCondition {
  id: string;
  fromColumn: string;
  operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN" | "NOT IN";
  value: string | string[];
  logicalOperator?: "AND" | "OR";
}

export interface UpdateFieldMapping {
  id: string;
  fromColumn: string;
  toColumn: string;
  transformFunction?: string;
  defaultValue?: string;
}

export interface WhereCondition {
  id: string;
  toColumn: string;
  operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN" | "NOT IN";
  valueSource: "from_column" | "static" | "current_timestamp";
  fromColumn?: string; // valueSource가 "from_column"인 경우
  staticValue?: string; // valueSource가 "static"인 경우
  logicalOperator?: "AND" | "OR";
}

export interface DeleteCondition {
  id: string;
  fromColumn: string;
  operator:
    | "="
    | "!="
    | ">"
    | "<"
    | ">="
    | "<="
    | "LIKE"
    | "IN"
    | "NOT IN"
    | "EXISTS"
    | "NOT EXISTS";
  value: string | string[];
  logicalOperator?: "AND" | "OR";
}

export interface DeleteWhereCondition {
  id: string;
  toColumn: string;
  operator: "=" | "!=" | ">" | "<" | ">=" | "<=" | "LIKE" | "IN" | "NOT IN";
  valueSource: "from_column" | "static" | "condition_result";
  fromColumn?: string;
  staticValue?: string;
  logicalOperator?: "AND" | "OR";
}

export interface DeleteSafetySettings {
  maxDeleteCount: number;
  requireConfirmation: boolean;
  dryRunFirst: boolean;
  logAllDeletes: boolean;
}

export interface ExecutionResult {
  success: boolean;
  message: string;
  executedActions?: any[];
  errors?: string[];
  warnings?: string[];
}

export class EnhancedDataflowControlService extends DataflowControlService {
  private multiConnectionService: MultiConnectionQueryService;

  constructor() {
    super();
    this.multiConnectionService = new MultiConnectionQueryService();
  }

  /**
   * 확장된 데이터플로우 제어 실행
   * 부모 클래스와 호환성을 위해 userId 파라미터 유지, 추가 파라미터는 옵션으로 전달
   */
  async executeDataflowControl(
    diagramId: number,
    relationshipId: string,
    triggerType: "insert" | "update" | "delete",
    sourceData: Record<string, any>,
    tableName: string,
    userId: string = "system", // 부모 클래스 시그니처 유지
    sourceConnectionId?: number,
    targetConnectionId?: number
  ): Promise<ExecutionResult> {
    try {
      logger.info(
        `확장된 데이터플로우 제어 실행 시작: diagram=${diagramId}, trigger=${triggerType}`
      );

      // 기본 실행 결과
      const result: ExecutionResult = {
        success: true,
        message: "데이터플로우 제어가 성공적으로 실행되었습니다.",
        executedActions: [],
        errors: [],
        warnings: [],
      };

      // 다이어그램 설정 조회
      const diagram = await this.getDiagramById(diagramId);
      if (!diagram) {
        return {
          success: false,
          message: "다이어그램을 찾을 수 없습니다.",
          errors: [`다이어그램 ID ${diagramId}를 찾을 수 없습니다.`],
        };
      }

      // 제어 계획 파싱
      const plan = this.parsePlan(diagram.plan);
      if (!plan.actions || plan.actions.length === 0) {
        return {
          success: true,
          message: "실행할 액션이 없습니다.",
          executedActions: [],
        };
      }

      // 각 액션 실행
      for (const action of plan.actions) {
        try {
          const enhancedAction = action as EnhancedControlAction;
          let actionResult: any;

          // 커넥션 ID 추출
          const sourceConnectionId = enhancedAction.fromConnection?.id || 0;
          const targetConnectionId = enhancedAction.toConnection?.id || 0;

          switch (enhancedAction.actionType) {
            case "insert":
              actionResult = await this.executeEnhancedMultiConnectionInsert(
                enhancedAction,
                sourceData,
                enhancedAction.fromTable,
                enhancedAction.targetTable || enhancedAction.fromTable,
                sourceConnectionId,
                targetConnectionId,
                null
              );
              break;

            case "update":
              actionResult = await this.executeEnhancedMultiConnectionUpdate(
                enhancedAction,
                sourceData,
                enhancedAction.fromTable,
                enhancedAction.targetTable || enhancedAction.fromTable,
                sourceConnectionId,
                targetConnectionId,
                null
              );
              break;

            case "delete":
              actionResult = await this.executeEnhancedMultiConnectionDelete(
                enhancedAction,
                sourceData,
                enhancedAction.fromTable,
                enhancedAction.targetTable || enhancedAction.fromTable,
                sourceConnectionId,
                targetConnectionId,
                null
              );
              break;

            default:
              throw new Error(
                `지원하지 않는 액션 타입입니다: ${enhancedAction.actionType}`
              );
          }

          result.executedActions!.push({
            actionId: enhancedAction.id,
            actionType: enhancedAction.actionType,
            result: actionResult,
          });
        } catch (actionError) {
          const errorMessage = `액션 ${action.id} 실행 실패: ${actionError instanceof Error ? actionError.message : actionError}`;
          logger.error(errorMessage);
          result.errors!.push(errorMessage);
        }
      }

      // 실행 결과 판정
      if (result.errors!.length > 0) {
        result.success = false;
        result.message = `일부 액션 실행에 실패했습니다. 성공: ${result.executedActions!.length}, 실패: ${result.errors!.length}`;
      }

      logger.info(
        `확장된 데이터플로우 제어 실행 완료: success=${result.success}`
      );
      return result;
    } catch (error) {
      logger.error(`확장된 데이터플로우 제어 실행 실패: ${error}`);
      return {
        success: false,
        message: "데이터플로우 제어 실행 중 오류가 발생했습니다.",
        errors: [error instanceof Error ? error.message : String(error)],
      };
    }
  }

  /**
   * 🆕 다중 커넥션 INSERT 실행
   */
  async executeEnhancedMultiConnectionInsert(
    action: ControlAction,
    sourceData: Record<string, any>,
    sourceTable: string,
    targetTable: string,
    fromConnectionId: number,
    toConnectionId: number,
    multiConnService: any
  ): Promise<any> {
    try {
      const enhancedAction = action as EnhancedControlAction;
      logger.info(`다중 커넥션 INSERT 실행: action=${action.id}`);

      // 커넥션 ID 결정
      const fromConnId = fromConnectionId || action.fromConnection?.id || 0;
      const toConnId = toConnectionId || action.toConnection?.id || 0;

      // FROM 테이블에서 소스 데이터 조회 (조건이 있는 경우)
      let fromData = sourceData;
      if (
        enhancedAction.fromTable &&
        action.conditions &&
        action.conditions.length > 0
      ) {
        const queryConditions = this.buildQueryConditions(
          action.conditions,
          sourceData
        );
        const fromResults =
          await this.multiConnectionService.fetchDataFromConnection(
            fromConnId,
            enhancedAction.fromTable,
            queryConditions
          );

        if (fromResults.length === 0) {
          logger.info(`FROM 테이블에서 조건에 맞는 데이터가 없습니다.`);
          return {
            inserted: 0,
            message: "조건에 맞는 소스 데이터가 없습니다.",
          };
        }

        fromData = fromResults[0]; // 첫 번째 결과 사용
      }

      // 필드 매핑 적용
      const mappedData = this.applyFieldMappings(
        action.fieldMappings || [],
        fromData
      );

      // TO 테이블에 데이터 삽입
      const insertResult =
        await this.multiConnectionService.insertDataToConnection(
          toConnId,
          action.targetTable || enhancedAction.fromTable,
          mappedData
        );

      logger.info(`다중 커넥션 INSERT 완료`);
      return insertResult;
    } catch (error) {
      logger.error(`다중 커넥션 INSERT 실패: ${error}`);
      throw error;
    }
  }

  /**
   * 🆕 다중 커넥션 UPDATE 실행
   */
  async executeEnhancedMultiConnectionUpdate(
    action: ControlAction,
    sourceData: Record<string, any>,
    sourceTable: string,
    targetTable: string,
    fromConnectionId: number,
    toConnectionId: number,
    multiConnService: any
  ): Promise<any> {
    try {
      const enhancedAction = action as EnhancedControlAction;
      logger.info(`다중 커넥션 UPDATE 실행: action=${action.id}`);

      // 커넥션 ID 결정
      const fromConnId = fromConnectionId || action.fromConnection?.id || 0;
      const toConnId = toConnectionId || action.toConnection?.id || 0;

      // UPDATE 조건 확인
      if (
        !enhancedAction.updateConditions ||
        enhancedAction.updateConditions.length === 0
      ) {
        throw new Error("UPDATE 작업에는 업데이트 조건이 필요합니다.");
      }

      // FROM 테이블에서 업데이트 조건 확인
      const updateConditions = this.buildUpdateConditions(
        enhancedAction.updateConditions,
        sourceData
      );
      const fromResults =
        await this.multiConnectionService.fetchDataFromConnection(
          fromConnId,
          enhancedAction.fromTable || action.targetTable || "default_table",
          updateConditions
        );

      if (fromResults.length === 0) {
        logger.info(`업데이트 조건에 맞는 데이터가 없습니다.`);
        return {
          updated: 0,
          message: "업데이트 조건에 맞는 데이터가 없습니다.",
        };
      }

      // 업데이트 필드 매핑 적용
      const updateData = this.applyUpdateFieldMappings(
        enhancedAction.updateFields || [],
        fromResults[0]
      );

      // WHERE 조건 구성 (TO 테이블 대상)
      const whereConditions = this.buildWhereConditions(
        enhancedAction.updateFields || [],
        fromResults[0]
      );

      // TO 테이블 데이터 업데이트
      const updateResult =
        await this.multiConnectionService.updateDataToConnection(
          toConnId,
          action.targetTable || enhancedAction.fromTable,
          updateData,
          whereConditions
        );

      logger.info(`다중 커넥션 UPDATE 완료`);
      return updateResult;
    } catch (error) {
      logger.error(`다중 커넥션 UPDATE 실패: ${error}`);
      throw error;
    }
  }

  /**
   * 🆕 다중 커넥션 DELETE 실행
   */
  async executeEnhancedMultiConnectionDelete(
    action: ControlAction,
    sourceData: Record<string, any>,
    sourceTable: string,
    targetTable: string,
    fromConnectionId: number,
    toConnectionId: number,
    multiConnService: any
  ): Promise<any> {
    try {
      const enhancedAction = action as EnhancedControlAction;
      logger.info(`다중 커넥션 DELETE 실행: action=${action.id}`);

      // 커넥션 ID 결정
      const fromConnId = fromConnectionId || action.fromConnection?.id || 0;
      const toConnId = toConnectionId || action.toConnection?.id || 0;

      // DELETE 조건 확인
      if (
        !enhancedAction.deleteConditions ||
        enhancedAction.deleteConditions.length === 0
      ) {
        throw new Error("DELETE 작업에는 삭제 조건이 필요합니다.");
      }

      // FROM 테이블에서 삭제 트리거 조건 확인
      const deleteConditions = this.buildDeleteConditions(
        enhancedAction.deleteConditions,
        sourceData
      );
      const fromResults =
        await this.multiConnectionService.fetchDataFromConnection(
          fromConnId,
          enhancedAction.fromTable || action.targetTable || "default_table",
          deleteConditions
        );

      if (fromResults.length === 0) {
        logger.info(`삭제 조건에 맞는 데이터가 없습니다.`);
        return { deleted: 0, message: "삭제 조건에 맞는 데이터가 없습니다." };
      }

      // WHERE 조건 구성 (TO 테이블 대상)
      const whereConditions = this.buildDeleteWhereConditions(
        enhancedAction.deleteWhereConditions || [],
        fromResults[0]
      );

      if (!whereConditions || Object.keys(whereConditions).length === 0) {
        throw new Error("DELETE 작업에는 WHERE 조건이 필수입니다.");
      }

      // 안전장치 적용
      const maxDeleteCount = enhancedAction.maxDeleteCount || 100;

      // Dry Run 실행 (선택사항)
      if (enhancedAction.dryRunFirst) {
        const countResult =
          await this.multiConnectionService.fetchDataFromConnection(
            toConnId,
            action.targetTable || enhancedAction.fromTable,
            whereConditions
          );

        logger.info(`삭제 예상 개수: ${countResult.length}건`);

        if (countResult.length > maxDeleteCount) {
          throw new Error(
            `삭제 대상이 ${countResult.length}건으로 최대 허용 개수(${maxDeleteCount})를 초과합니다.`
          );
        }
      }

      // TO 테이블에서 데이터 삭제
      const deleteResult =
        await this.multiConnectionService.deleteDataFromConnection(
          toConnId,
          action.targetTable || enhancedAction.fromTable,
          whereConditions,
          maxDeleteCount
        );

      // 삭제 로그 기록 (선택사항)
      if (enhancedAction.logAllDeletes) {
        logger.info(
          `삭제 실행 로그: ${JSON.stringify({
            action: action.id,
            deletedCount: deleteResult.length,
            conditions: whereConditions,
          })}`
        );
      }

      logger.info(`다중 커넥션 DELETE 완료`);
      return deleteResult;
    } catch (error) {
      logger.error(`다중 커넥션 DELETE 실패: ${error}`);
      throw error;
    }
  }

  /**
   * 쿼리 조건 구성
   */
  private buildQueryConditions(
    conditions: ControlCondition[],
    sourceData: Record<string, any>
  ): Record<string, any> {
    const queryConditions: Record<string, any> = {};

    conditions.forEach((condition) => {
      if (condition.type === "condition" && condition.field) {
        let value = condition.value;

        // 소스 데이터에서 값 참조
        if (
          typeof value === "string" &&
          value.startsWith("${") &&
          value.endsWith("}")
        ) {
          const fieldName = value.slice(2, -1);
          value = sourceData[fieldName];
        }

        queryConditions[condition.field] = value;
      }
    });

    return queryConditions;
  }

  /**
   * UPDATE 조건 구성
   */
  private buildUpdateConditions(
    updateConditions: UpdateCondition[],
    sourceData: Record<string, any>
  ): Record<string, any> {
    const conditions: Record<string, any> = {};

    updateConditions.forEach((condition) => {
      let value = condition.value;

      // 소스 데이터에서 값 참조
      if (
        typeof value === "string" &&
        value.startsWith("${") &&
        value.endsWith("}")
      ) {
        const fieldName = value.slice(2, -1);
        value = sourceData[fieldName];
      }

      conditions[condition.fromColumn] = value;
    });

    return conditions;
  }

  /**
   * UPDATE 필드 매핑 적용
   */
  private applyUpdateFieldMappings(
    updateFields: UpdateFieldMapping[],
    fromData: Record<string, any>
  ): Record<string, any> {
    const updateData: Record<string, any> = {};

    updateFields.forEach((mapping) => {
      let value = fromData[mapping.fromColumn];

      // 기본값 사용
      if (value === undefined || value === null) {
        value = mapping.defaultValue;
      }

      // 변환 함수 적용 (추후 구현 가능)
      if (mapping.transformFunction) {
        // TODO: 변환 함수 로직 구현
      }

      updateData[mapping.toColumn] = value;
    });

    return updateData;
  }

  /**
   * WHERE 조건 구성 (UPDATE용)
   */
  private buildWhereConditions(
    updateFields: UpdateFieldMapping[],
    fromData: Record<string, any>
  ): Record<string, any> {
    const whereConditions: Record<string, any> = {};

    // 기본적으로 ID 필드로 WHERE 조건 구성
    if (fromData.id) {
      whereConditions.id = fromData.id;
    }

    return whereConditions;
  }

  /**
   * DELETE 조건 구성
   */
  private buildDeleteConditions(
    deleteConditions: DeleteCondition[],
    sourceData: Record<string, any>
  ): Record<string, any> {
    const conditions: Record<string, any> = {};

    deleteConditions.forEach((condition) => {
      let value = condition.value;

      // 소스 데이터에서 값 참조
      if (
        typeof value === "string" &&
        value.startsWith("${") &&
        value.endsWith("}")
      ) {
        const fieldName = value.slice(2, -1);
        value = sourceData[fieldName];
      }

      conditions[condition.fromColumn] = value;
    });

    return conditions;
  }

  /**
   * DELETE WHERE 조건 구성
   */
  private buildDeleteWhereConditions(
    whereConditions: DeleteWhereCondition[],
    fromData: Record<string, any>
  ): Record<string, any> {
    const conditions: Record<string, any> = {};

    whereConditions.forEach((condition) => {
      let value: any;

      switch (condition.valueSource) {
        case "from_column":
          if (condition.fromColumn) {
            value = fromData[condition.fromColumn];
          }
          break;
        case "static":
          value = condition.staticValue;
          break;
        case "condition_result":
          // 조건 결과를 사용 (추후 구현)
          break;
      }

      if (value !== undefined && value !== null) {
        conditions[condition.toColumn] = value;
      }
    });

    return conditions;
  }

  /**
   * 필드 매핑 적용
   */
  private applyFieldMappings(
    fieldMappings: any[],
    sourceData: Record<string, any>
  ): Record<string, any> {
    const mappedData: Record<string, any> = {};

    fieldMappings.forEach((mapping) => {
      let value: any;

      if (mapping.sourceField) {
        value = sourceData[mapping.sourceField];
      } else if (mapping.defaultValue !== undefined) {
        value = mapping.defaultValue;
      }

      if (value !== undefined) {
        mappedData[mapping.targetField] = value;
      }
    });

    return mappedData;
  }

  /**
   * 다이어그램 조회 (부모 클래스에서 가져오기)
   */
  private async getDiagramById(diagramId: number): Promise<any> {
    // 부모 클래스의 메서드 호출 또는 직접 구현
    // 임시로 간단한 구현
    return { id: diagramId, plan: "{}" };
  }

  /**
   * 계획 파싱 (부모 클래스에서 가져오기)
   */
  private parsePlan(planJson: string): any {
    try {
      return JSON.parse(planJson);
    } catch (error) {
      logger.error(`계획 파싱 실패: ${error}`);
      return { actions: [] };
    }
  }
}
