import { query, queryOne } from "../database/db";
import { logger } from "../utils/logger";

// 외부 호출 설정 타입 정의
export interface ExternalCallConfig {
  id?: number;
  config_name: string;
  call_type: string;
  api_type?: string;
  config_data: any;
  description?: string;
  company_code?: string;
  is_active?: string;
  created_by?: string;
  updated_by?: string;
}

export interface ExternalCallConfigFilter {
  company_code?: string;
  call_type?: string;
  api_type?: string;
  is_active?: string;
  search?: string;
}

export class ExternalCallConfigService {
  /**
   * 외부 호출 설정 목록 조회
   */
  async getConfigs(
    filter: ExternalCallConfigFilter = {}
  ): Promise<ExternalCallConfig[]> {
    try {
      logger.info("=== 외부 호출 설정 목록 조회 시작 ===");
      logger.info(`필터 조건:`, filter);

      const conditions: string[] = [];
      const params: any[] = [];
      let paramIndex = 1;

      // 회사 코드 필터
      if (filter.company_code) {
        conditions.push(`company_code = $${paramIndex++}`);
        params.push(filter.company_code);
      }

      // 호출 타입 필터
      if (filter.call_type) {
        conditions.push(`call_type = $${paramIndex++}`);
        params.push(filter.call_type);
      }

      // API 타입 필터
      if (filter.api_type) {
        conditions.push(`api_type = $${paramIndex++}`);
        params.push(filter.api_type);
      }

      // 활성화 상태 필터
      if (filter.is_active) {
        conditions.push(`is_active = $${paramIndex++}`);
        params.push(filter.is_active);
      }

      // 검색어 필터 (설정 이름 또는 설명)
      if (filter.search) {
        conditions.push(
          `(config_name ILIKE $${paramIndex} OR description ILIKE $${paramIndex})`
        );
        params.push(`%${filter.search}%`);
        paramIndex++;
      }

      const whereClause =
        conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";

      const configs = await query<ExternalCallConfig>(
        `SELECT * FROM external_call_configs
         ${whereClause}
         ORDER BY is_active DESC, created_date DESC`,
        params
      );

      logger.info(`외부 호출 설정 조회 결과: ${configs.length}개`);
      return configs;
    } catch (error) {
      logger.error("외부 호출 설정 목록 조회 실패:", error);
      throw error;
    }
  }

  /**
   * 외부 호출 설정 단일 조회
   */
  async getConfigById(id: number): Promise<ExternalCallConfig | null> {
    try {
      logger.info(`=== 외부 호출 설정 조회: ID ${id} ===`);

      const config = await queryOne<ExternalCallConfig>(
        `SELECT * FROM external_call_configs WHERE id = $1`,
        [id]
      );

      if (config) {
        logger.info(`외부 호출 설정 조회 성공: ${config.config_name}`);
      } else {
        logger.warn(`외부 호출 설정을 찾을 수 없음: ID ${id}`);
      }

      return config || null;
    } catch (error) {
      logger.error(`외부 호출 설정 조회 실패 (ID: ${id}):`, error);
      throw error;
    }
  }

  /**
   * 외부 호출 설정 생성
   */
  async createConfig(data: ExternalCallConfig): Promise<ExternalCallConfig> {
    try {
      logger.info("=== 외부 호출 설정 생성 시작 ===");
      logger.info(`생성할 설정:`, {
        config_name: data.config_name,
        call_type: data.call_type,
        api_type: data.api_type,
        company_code: data.company_code || "*",
      });

      // 중복 이름 검사
      const existingConfig = await queryOne<ExternalCallConfig>(
        `SELECT * FROM external_call_configs 
         WHERE config_name = $1 AND company_code = $2 AND is_active = $3`,
        [data.config_name, data.company_code || "*", "Y"]
      );

      if (existingConfig) {
        throw new Error(
          `동일한 이름의 외부 호출 설정이 이미 존재합니다: ${data.config_name}`
        );
      }

      const newConfig = await queryOne<ExternalCallConfig>(
        `INSERT INTO external_call_configs 
         (config_name, call_type, api_type, config_data, description, 
          company_code, is_active, created_by, updated_by, created_date, updated_date)
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW(), NOW())
         RETURNING *`,
        [
          data.config_name,
          data.call_type,
          data.api_type,
          JSON.stringify(data.config_data),
          data.description,
          data.company_code || "*",
          data.is_active || "Y",
          data.created_by,
          data.updated_by,
        ]
      );

      logger.info(
        `외부 호출 설정 생성 완료: ${newConfig!.config_name} (ID: ${newConfig!.id})`
      );
      return newConfig!;
    } catch (error) {
      logger.error("외부 호출 설정 생성 실패:", error);
      throw error;
    }
  }

  /**
   * 외부 호출 설정 수정
   */
  async updateConfig(
    id: number,
    data: Partial<ExternalCallConfig>
  ): Promise<ExternalCallConfig> {
    try {
      logger.info(`=== 외부 호출 설정 수정 시작: ID ${id} ===`);

      // 기존 설정 존재 확인
      const existingConfig = await this.getConfigById(id);
      if (!existingConfig) {
        throw new Error(`외부 호출 설정을 찾을 수 없습니다: ID ${id}`);
      }

      // 이름 중복 검사 (다른 설정과 중복되는지)
      if (data.config_name && data.config_name !== existingConfig.config_name) {
        const duplicateConfig = await queryOne<ExternalCallConfig>(
          `SELECT * FROM external_call_configs 
           WHERE config_name = $1 AND company_code = $2 AND is_active = $3 AND id != $4`,
          [
            data.config_name,
            data.company_code || existingConfig.company_code,
            "Y",
            id,
          ]
        );

        if (duplicateConfig) {
          throw new Error(
            `동일한 이름의 외부 호출 설정이 이미 존재합니다: ${data.config_name}`
          );
        }
      }

      // 동적 UPDATE 쿼리 생성
      const updateFields: string[] = ["updated_date = NOW()"];
      const params: any[] = [];
      let paramIndex = 1;

      if (data.config_name) {
        updateFields.push(`config_name = $${paramIndex++}`);
        params.push(data.config_name);
      }
      if (data.call_type) {
        updateFields.push(`call_type = $${paramIndex++}`);
        params.push(data.call_type);
      }
      if (data.api_type !== undefined) {
        updateFields.push(`api_type = $${paramIndex++}`);
        params.push(data.api_type);
      }
      if (data.config_data) {
        updateFields.push(`config_data = $${paramIndex++}`);
        params.push(JSON.stringify(data.config_data));
      }
      if (data.description !== undefined) {
        updateFields.push(`description = $${paramIndex++}`);
        params.push(data.description);
      }
      if (data.company_code) {
        updateFields.push(`company_code = $${paramIndex++}`);
        params.push(data.company_code);
      }
      if (data.is_active) {
        updateFields.push(`is_active = $${paramIndex++}`);
        params.push(data.is_active);
      }
      if (data.updated_by) {
        updateFields.push(`updated_by = $${paramIndex++}`);
        params.push(data.updated_by);
      }

      params.push(id);

      const updatedConfig = await queryOne<ExternalCallConfig>(
        `UPDATE external_call_configs 
         SET ${updateFields.join(", ")}
         WHERE id = $${paramIndex}
         RETURNING *`,
        params
      );

      logger.info(
        `외부 호출 설정 수정 완료: ${updatedConfig!.config_name} (ID: ${id})`
      );
      return updatedConfig!;
    } catch (error) {
      logger.error(`외부 호출 설정 수정 실패 (ID: ${id}):`, error);
      throw error;
    }
  }

  /**
   * 외부 호출 설정 삭제 (논리 삭제)
   */
  async deleteConfig(id: number, deletedBy?: string): Promise<void> {
    try {
      logger.info(`=== 외부 호출 설정 삭제 시작: ID ${id} ===`);

      // 기존 설정 존재 확인
      const existingConfig = await this.getConfigById(id);
      if (!existingConfig) {
        throw new Error(`외부 호출 설정을 찾을 수 없습니다: ID ${id}`);
      }

      // 논리 삭제 (is_active = 'N')
      await query(
        `UPDATE external_call_configs 
         SET is_active = $1, updated_by = $2, updated_date = NOW()
         WHERE id = $3`,
        ["N", deletedBy, id]
      );

      logger.info(
        `외부 호출 설정 삭제 완료: ${existingConfig.config_name} (ID: ${id})`
      );
    } catch (error) {
      logger.error(`외부 호출 설정 삭제 실패 (ID: ${id}):`, error);
      throw error;
    }
  }

  /**
   * 외부 호출 설정 테스트
   */
  async testConfig(id: number): Promise<{ success: boolean; message: string }> {
    try {
      logger.info(`=== 외부 호출 설정 테스트 시작: ID ${id} ===`);

      const config = await this.getConfigById(id);
      if (!config) {
        throw new Error(`외부 호출 설정을 찾을 수 없습니다: ID ${id}`);
      }

      // TODO: ExternalCallService를 사용하여 실제 테스트 호출
      // 현재는 기본적인 검증만 수행
      const configData = config.config_data as any;

      let isValid = true;
      let validationMessage = "";

      switch (config.api_type) {
        case "discord":
          if (!configData.webhookUrl) {
            isValid = false;
            validationMessage = "Discord 웹훅 URL이 필요합니다.";
          }
          break;
        case "slack":
          if (!configData.webhookUrl) {
            isValid = false;
            validationMessage = "Slack 웹훅 URL이 필요합니다.";
          }
          break;
        case "kakao-talk":
          if (!configData.accessToken) {
            isValid = false;
            validationMessage = "카카오톡 액세스 토큰이 필요합니다.";
          }
          break;
        default:
          if (config.call_type === "rest-api" && !configData.url) {
            isValid = false;
            validationMessage = "API URL이 필요합니다.";
          }
      }

      if (!isValid) {
        logger.warn(`외부 호출 설정 테스트 실패: ${validationMessage}`);
        return { success: false, message: validationMessage };
      }

      logger.info(`외부 호출 설정 테스트 성공: ${config.config_name}`);
      return { success: true, message: "설정이 유효합니다." };
    } catch (error) {
      logger.error(`외부 호출 설정 테스트 실패 (ID: ${id}):`, error);
      return {
        success: false,
        message: error instanceof Error ? error.message : "테스트 실패",
      };
    }
  }

  /**
   * 🔥 데이터 매핑과 함께 외부호출 실행
   */
  async executeConfigWithDataMapping(
    configId: number,
    requestData: Record<string, any>,
    contextData: Record<string, any>
  ): Promise<{
    success: boolean;
    message: string;
    data?: any;
    executionTime: number;
    error?: string;
  }> {
    const startTime = performance.now();

    try {
      logger.info(`=== 외부호출 실행 시작 (ID: ${configId}) ===`);

      // 1. 설정 조회
      const config = await this.getConfigById(configId);
      if (!config) {
        throw new Error(`외부호출 설정을 찾을 수 없습니다: ${configId}`);
      }

      // 2. 데이터 매핑 처리 (있는 경우)
      let processedData = requestData;
      const configData = config.config_data as any;
      if (configData?.dataMappingConfig?.outboundMapping) {
        logger.info("Outbound 데이터 매핑 처리 중...");
        processedData = await this.processOutboundMapping(
          configData.dataMappingConfig.outboundMapping,
          requestData
        );
      }

      // 3. 외부 API 호출
      const callResult = await this.executeExternalCall(
        config,
        processedData,
        contextData
      );

      // 4. Inbound 데이터 매핑 처리 (있는 경우)
      if (callResult.success && configData?.dataMappingConfig?.inboundMapping) {
        logger.info("Inbound 데이터 매핑 처리 중...");
        await this.processInboundMapping(
          configData.dataMappingConfig.inboundMapping,
          callResult.data
        );
      }

      const executionTime = performance.now() - startTime;
      logger.info(`외부호출 실행 완료: ${executionTime.toFixed(2)}ms`);

      return {
        success: callResult.success,
        message: callResult.success
          ? `외부호출 '${config.config_name}' 실행 완료`
          : `외부호출 '${config.config_name}' 실행 실패`,
        data: callResult.data,
        executionTime,
        error: callResult.error,
      };
    } catch (error) {
      const executionTime = performance.now() - startTime;
      logger.error("외부호출 실행 실패:", error);

      const errorMessage =
        error instanceof Error ? error.message : "알 수 없는 오류";

      return {
        success: false,
        message: `외부호출 실행 실패: ${errorMessage}`,
        executionTime,
        error: errorMessage,
      };
    }
  }

  /**
   * 🔥 버튼 제어용 외부호출 설정 목록 조회 (간소화된 정보)
   */
  async getConfigsForButtonControl(companyCode: string): Promise<
    Array<{
      id: string;
      name: string;
      description?: string;
      apiUrl: string;
      method: string;
      hasDataMapping: boolean;
    }>
  > {
    try {
      const configs = await query<{
        id: number;
        config_name: string;
        description: string | null;
        config_data: any;
      }>(
        `SELECT id, config_name, description, config_data 
         FROM external_call_configs
         WHERE company_code = $1 AND is_active = $2
         ORDER BY config_name ASC`,
        [companyCode, "Y"]
      );

      return configs.map((config) => {
        const configData = config.config_data as any;
        return {
          id: config.id.toString(),
          name: config.config_name,
          description: config.description || undefined,
          apiUrl: configData?.restApiSettings?.apiUrl || "",
          method: configData?.restApiSettings?.httpMethod || "GET",
          hasDataMapping: !!configData?.dataMappingConfig,
        };
      });
    } catch (error) {
      logger.error("버튼 제어용 외부호출 설정 조회 실패:", error);
      throw error;
    }
  }

  /**
   * 🔥 실제 외부 API 호출 실행
   */
  private async executeExternalCall(
    config: ExternalCallConfig,
    requestData: Record<string, any>,
    contextData: Record<string, any>
  ): Promise<{ success: boolean; data?: any; error?: string }> {
    try {
      const configData = config.config_data as any;
      const restApiSettings = configData?.restApiSettings;
      if (!restApiSettings) {
        throw new Error("REST API 설정이 없습니다.");
      }

      const {
        apiUrl,
        httpMethod,
        headers = {},
        timeout = 30000,
      } = restApiSettings;

      // 요청 헤더 준비
      const requestHeaders = {
        "Content-Type": "application/json",
        ...headers,
      };

      // 인증 처리
      if (restApiSettings.authentication?.type === "basic") {
        const { username, password } = restApiSettings.authentication;
        const credentials = Buffer.from(`${username}:${password}`).toString(
          "base64"
        );
        requestHeaders["Authorization"] = `Basic ${credentials}`;
      } else if (restApiSettings.authentication?.type === "bearer") {
        const { token } = restApiSettings.authentication;
        requestHeaders["Authorization"] = `Bearer ${token}`;
      }

      // 요청 본문 준비
      let requestBody = undefined;
      if (["POST", "PUT", "PATCH"].includes(httpMethod.toUpperCase())) {
        requestBody = JSON.stringify({
          ...requestData,
          _context: contextData, // 컨텍스트 정보 추가
        });
      }

      logger.info(`외부 API 호출: ${httpMethod} ${apiUrl}`);

      // 실제 HTTP 요청 (여기서는 간단한 예시)
      // 실제 구현에서는 axios나 fetch를 사용
      const response = await fetch(apiUrl, {
        method: httpMethod,
        headers: requestHeaders,
        body: requestBody,
        signal: AbortSignal.timeout(timeout),
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const responseData = await response.json();

      return {
        success: true,
        data: responseData,
      };
    } catch (error) {
      logger.error("외부 API 호출 실패:", error);
      const errorMessage =
        error instanceof Error ? error.message : "알 수 없는 오류";
      return {
        success: false,
        error: errorMessage,
      };
    }
  }

  /**
   * 🔥 Outbound 데이터 매핑 처리
   */
  private async processOutboundMapping(
    mapping: any,
    sourceData: Record<string, any>
  ): Promise<Record<string, any>> {
    try {
      // 간단한 매핑 로직 (실제로는 더 복잡한 변환 로직 필요)
      const mappedData: Record<string, any> = {};

      if (mapping.fieldMappings) {
        for (const fieldMapping of mapping.fieldMappings) {
          const { sourceField, targetField, transformation } = fieldMapping;

          let value = sourceData[sourceField];

          // 변환 로직 적용
          if (transformation) {
            switch (transformation.type) {
              case "format":
                // 포맷 변환 로직
                break;
              case "calculate":
                // 계산 로직
                break;
              default:
                // 기본값 그대로 사용
                break;
            }
          }

          mappedData[targetField] = value;
        }
      }

      return mappedData;
    } catch (error) {
      logger.error("Outbound 데이터 매핑 처리 실패:", error);
      return sourceData; // 실패 시 원본 데이터 반환
    }
  }

  /**
   * 🔥 Inbound 데이터 매핑 처리
   */
  private async processInboundMapping(
    mapping: any,
    responseData: any
  ): Promise<void> {
    try {
      // Inbound 매핑 로직 (응답 데이터를 내부 시스템에 저장)
      logger.info("Inbound 데이터 매핑 처리:", mapping);

      // 실제 구현에서는 응답 데이터를 파싱하여 내부 테이블에 저장하는 로직 필요
      // 예: 외부 API에서 받은 사용자 정보를 내부 사용자 테이블에 업데이트
    } catch (error) {
      logger.error("Inbound 데이터 매핑 처리 실패:", error);
      // Inbound 매핑 실패는 전체 플로우를 중단하지 않음
    }
  }
}

export default new ExternalCallConfigService();
