import { Pool, QueryResult } from "pg";
import axios, { AxiosResponse } from "axios";
import https from "https";
import { getPool } from "../database/db";
import logger from "../utils/logger";
import {
  ExternalRestApiConnection,
  ExternalRestApiConnectionFilter,
  RestApiTestRequest,
  RestApiTestResult,
  AuthType,
} from "../types/externalRestApiTypes";
import { ApiResponse } from "../types/common";
import crypto from "crypto";

const pool = getPool();

// 암호화 설정
const ENCRYPTION_KEY =
  process.env.DB_PASSWORD_SECRET || "default-secret-key-change-in-production";
const ALGORITHM = "aes-256-gcm";

export class ExternalRestApiConnectionService {
  /**
   * REST API 연결 목록 조회
   */
  static async getConnections(
    filter: ExternalRestApiConnectionFilter = {},
    userCompanyCode?: string
  ): Promise<ApiResponse<ExternalRestApiConnection[]>> {
    try {
      let query = `
        SELECT 
          e.id, e.connection_name, e.description, e.base_url, e.endpoint_path, e.default_headers,
          e.default_method,
          -- DB 스키마의 컬럼명은 default_request_body 기준이고
          -- 코드에서는 default_body 필드로 사용하기 위해 alias 처리
          e.default_request_body AS default_body,
          e.auth_type, e.auth_config, e.timeout, e.retry_count, e.retry_delay,
          e.company_code, e.is_active, e.created_date, e.created_by,
          e.updated_date, e.updated_by, e.last_test_date, e.last_test_result, e.last_test_message,
          COALESCE(c.company_name, CASE WHEN e.company_code = '*' THEN '전체' ELSE e.company_code END) AS company_name
        FROM external_rest_api_connections e
        LEFT JOIN company_mng c ON e.company_code = c.company_code
        WHERE 1=1
      `;

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

      // 회사별 필터링 (최고 관리자가 아닌 경우 필수)
      if (userCompanyCode && userCompanyCode !== "*") {
        query += ` AND e.company_code = $${paramIndex}`;
        params.push(userCompanyCode);
        paramIndex++;
        logger.info(`회사별 REST API 연결 필터링: ${userCompanyCode}`);
      } else if (userCompanyCode === "*") {
        logger.info(`최고 관리자: 모든 REST API 연결 조회`);
        // 필터가 있으면 적용
        if (filter.company_code) {
          query += ` AND e.company_code = $${paramIndex}`;
          params.push(filter.company_code);
          paramIndex++;
        }
      } else {
        // userCompanyCode가 없는 경우 (하위 호환성)
        if (filter.company_code) {
          query += ` AND e.company_code = $${paramIndex}`;
          params.push(filter.company_code);
          paramIndex++;
        }
      }

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

      // 인증 타입 필터
      if (filter.auth_type) {
        query += ` AND e.auth_type = $${paramIndex}`;
        params.push(filter.auth_type);
        paramIndex++;
      }

      // 검색어 필터 (연결명, 설명, URL)
      if (filter.search) {
        query += ` AND (
          e.connection_name ILIKE $${paramIndex} OR 
          e.description ILIKE $${paramIndex} OR 
          e.base_url ILIKE $${paramIndex}
        )`;
        params.push(`%${filter.search}%`);
        paramIndex++;
      }

      query += ` ORDER BY created_date DESC`;

      const result: QueryResult<any> = await pool.query(query, params);

      // 민감 정보 복호화
      const connections = result.rows.map((row: any) => ({
        ...row,
        auth_config: row.auth_config
          ? this.decryptSensitiveData(row.auth_config)
          : null,
      }));

      return {
        success: true,
        data: connections,
        message: `${connections.length}개의 연결을 조회했습니다.`,
      };
    } catch (error) {
      logger.error("REST API 연결 목록 조회 오류:", error);
      return {
        success: false,
        message: "연결 목록 조회에 실패했습니다.",
        error: {
          code: "FETCH_ERROR",
          details: error instanceof Error ? error.message : "알 수 없는 오류",
        },
      };
    }
  }

  /**
   * REST API 연결 상세 조회
   */
  static async getConnectionById(
    id: number,
    userCompanyCode?: string
  ): Promise<ApiResponse<ExternalRestApiConnection>> {
    try {
      let query = `
        SELECT 
          id, connection_name, description, base_url, endpoint_path, default_headers,
          default_method,
          default_request_body AS default_body,
          auth_type, auth_config, timeout, retry_count, retry_delay,
          company_code, is_active, created_date, created_by,
          updated_date, updated_by, last_test_date, last_test_result, last_test_message
        FROM external_rest_api_connections
        WHERE id = $1
      `;

      const params: any[] = [id];

      // 회사별 필터링 (최고 관리자가 아닌 경우)
      if (userCompanyCode && userCompanyCode !== "*") {
        query += ` AND company_code = $2`;
        params.push(userCompanyCode);
      }

      const result: QueryResult<any> = await pool.query(query, params);

      if (result.rows.length === 0) {
        return {
          success: false,
          message: "연결을 찾을 수 없거나 권한이 없습니다.",
        };
      }

      const connection = result.rows[0];
      connection.auth_config = connection.auth_config
        ? this.decryptSensitiveData(connection.auth_config)
        : null;

      // 디버깅: 조회된 연결 정보 로깅
      logger.info(`REST API 연결 조회 결과 (ID: ${id}): connection_name=${connection.connection_name}, default_method=${connection.default_method}, endpoint_path=${connection.endpoint_path}`);

      return {
        success: true,
        data: connection,
        message: "연결을 조회했습니다.",
      };
    } catch (error) {
      logger.error("REST API 연결 상세 조회 오류:", error);
      return {
        success: false,
        message: "연결 조회에 실패했습니다.",
        error: {
          code: "FETCH_ERROR",
          details: error instanceof Error ? error.message : "알 수 없는 오류",
        },
      };
    }
  }

  /**
   * REST API 연결 생성
   */
  static async createConnection(
    data: ExternalRestApiConnection
  ): Promise<ApiResponse<ExternalRestApiConnection>> {
    try {
      // 유효성 검증
      this.validateConnectionData(data);

      // 민감 정보 암호화
      const encryptedAuthConfig = data.auth_config
        ? this.encryptSensitiveData(data.auth_config)
        : null;

      const query = `
        INSERT INTO external_rest_api_connections (
          connection_name, description, base_url, endpoint_path, default_headers,
          default_method, default_request_body,
          auth_type, auth_config, timeout, retry_count, retry_delay,
          company_code, is_active, created_by, save_to_history
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
        RETURNING *
      `;

      const params = [
        data.connection_name,
        data.description || null,
        data.base_url,
        data.endpoint_path || null,
        JSON.stringify(data.default_headers || {}),
        data.default_method || "GET",
        data.default_body || null,
        data.auth_type,
        encryptedAuthConfig ? JSON.stringify(encryptedAuthConfig) : null,
        data.timeout || 30000,
        data.retry_count || 0,
        data.retry_delay || 1000,
        data.company_code || "*",
        data.is_active || "Y",
        data.created_by || "system",
        data.save_to_history || "N",
      ];

      // 디버깅: 저장하려는 데이터 로깅
      logger.info(`REST API 연결 생성 요청 데이터:`, {
        connection_name: data.connection_name,
        company_code: data.company_code,
        default_method: data.default_method,
        endpoint_path: data.endpoint_path,
        base_url: data.base_url,
        default_body: data.default_body ? "있음" : "없음",
      });

      const result: QueryResult<any> = await pool.query(query, params);

      logger.info(`REST API 연결 생성 성공: ${data.connection_name}`);

      return {
        success: true,
        data: result.rows[0],
        message: "연결이 생성되었습니다.",
      };
    } catch (error: any) {
      logger.error("REST API 연결 생성 오류:", error);

      // 중복 키 오류 처리
      if (error.code === "23505") {
        return {
          success: false,
          message: "이미 존재하는 연결명입니다.",
        };
      }

      return {
        success: false,
        message: "연결 생성에 실패했습니다.",
        error: {
          code: "CREATE_ERROR",
          details: error instanceof Error ? error.message : "알 수 없는 오류",
        },
      };
    }
  }

  /**
   * REST API 연결 수정
   */
  static async updateConnection(
    id: number,
    data: Partial<ExternalRestApiConnection>,
    userCompanyCode?: string
  ): Promise<ApiResponse<ExternalRestApiConnection>> {
    try {
      // 기존 연결 확인 (회사 코드로 권한 체크)
      const existing = await this.getConnectionById(id, userCompanyCode);
      if (!existing.success) {
        return existing;
      }

      // 민감 정보 암호화
      const encryptedAuthConfig = data.auth_config
        ? this.encryptSensitiveData(data.auth_config)
        : undefined;

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

      if (data.connection_name !== undefined) {
        updateFields.push(`connection_name = $${paramIndex}`);
        params.push(data.connection_name);
        paramIndex++;
      }

      if (data.description !== undefined) {
        updateFields.push(`description = $${paramIndex}`);
        params.push(data.description);
        paramIndex++;
      }

      if (data.base_url !== undefined) {
        updateFields.push(`base_url = $${paramIndex}`);
        params.push(data.base_url);
        paramIndex++;
      }

      if (data.endpoint_path !== undefined) {
        updateFields.push(`endpoint_path = $${paramIndex}`);
        params.push(data.endpoint_path);
        paramIndex++;
      }

      if (data.default_headers !== undefined) {
        updateFields.push(`default_headers = $${paramIndex}`);
        params.push(JSON.stringify(data.default_headers));
        paramIndex++;
      }

      if (data.default_method !== undefined) {
        updateFields.push(`default_method = $${paramIndex}`);
        params.push(data.default_method);
        paramIndex++;
        logger.info(`수정 요청 - default_method: ${data.default_method}`);
      }

      if (data.default_body !== undefined) {
        updateFields.push(`default_request_body = $${paramIndex}`);
        params.push(data.default_body); // null이면 DB에서 NULL로 저장됨
        paramIndex++;
        logger.info(`수정 요청 - default_body: ${data.default_body ? "있음" : "삭제(null)"}`);
      }

      if (data.auth_type !== undefined) {
        updateFields.push(`auth_type = $${paramIndex}`);
        params.push(data.auth_type);
        paramIndex++;
      }

      if (encryptedAuthConfig !== undefined) {
        updateFields.push(`auth_config = $${paramIndex}`);
        params.push(JSON.stringify(encryptedAuthConfig));
        paramIndex++;
      }

      if (data.timeout !== undefined) {
        updateFields.push(`timeout = $${paramIndex}`);
        params.push(data.timeout);
        paramIndex++;
      }

      if (data.retry_count !== undefined) {
        updateFields.push(`retry_count = $${paramIndex}`);
        params.push(data.retry_count);
        paramIndex++;
      }

      if (data.retry_delay !== undefined) {
        updateFields.push(`retry_delay = $${paramIndex}`);
        params.push(data.retry_delay);
        paramIndex++;
      }

      if (data.is_active !== undefined) {
        updateFields.push(`is_active = $${paramIndex}`);
        params.push(data.is_active);
        paramIndex++;
      }

      if (data.save_to_history !== undefined) {
        updateFields.push(`save_to_history = $${paramIndex}`);
        params.push(data.save_to_history);
        paramIndex++;
      }

      if (data.updated_by !== undefined) {
        updateFields.push(`updated_by = $${paramIndex}`);
        params.push(data.updated_by);
        paramIndex++;
      }

      updateFields.push(`updated_date = NOW()`);

      params.push(id);

      const query = `
        UPDATE external_rest_api_connections
        SET ${updateFields.join(", ")}
        WHERE id = $${paramIndex}
        RETURNING *
      `;

      const result: QueryResult<any> = await pool.query(query, params);

      logger.info(`REST API 연결 수정 성공: ID ${id}`);

      return {
        success: true,
        data: result.rows[0],
        message: "연결이 수정되었습니다.",
      };
    } catch (error: any) {
      logger.error("REST API 연결 수정 오류:", error);

      if (error.code === "23505") {
        return {
          success: false,
          message: "이미 존재하는 연결명입니다.",
        };
      }

      return {
        success: false,
        message: "연결 수정에 실패했습니다.",
        error: {
          code: "UPDATE_ERROR",
          details: error instanceof Error ? error.message : "알 수 없는 오류",
        },
      };
    }
  }

  /**
   * REST API 연결 삭제
   */
  static async deleteConnection(
    id: number,
    userCompanyCode?: string
  ): Promise<ApiResponse<void>> {
    try {
      let query = `
        DELETE FROM external_rest_api_connections
        WHERE id = $1
      `;

      const params: any[] = [id];

      // 회사별 필터링 (최고 관리자가 아닌 경우)
      if (userCompanyCode && userCompanyCode !== "*") {
        query += ` AND company_code = $2`;
        params.push(userCompanyCode);
      }

      query += ` RETURNING connection_name`;

      const result: QueryResult<any> = await pool.query(query, params);

      if (result.rows.length === 0) {
        return {
          success: false,
          message: "연결을 찾을 수 없거나 권한이 없습니다.",
        };
      }

      logger.info(
        `REST API 연결 삭제 성공: ${result.rows[0].connection_name} (회사: ${userCompanyCode || "전체"})`
      );

      return {
        success: true,
        message: "연결이 삭제되었습니다.",
      };
    } catch (error) {
      logger.error("REST API 연결 삭제 오류:", error);
      return {
        success: false,
        message: "연결 삭제에 실패했습니다.",
        error: {
          code: "DELETE_ERROR",
          details: error instanceof Error ? error.message : "알 수 없는 오류",
        },
      };
    }
  }

  /**
   * 인증 헤더 생성
   */
  static async getAuthHeaders(
    authType: AuthType,
    authConfig: any,
    companyCode?: string
  ): Promise<Record<string, string>> {
    const headers: Record<string, string> = {};

    if (authType === "db-token") {
      const cfg = authConfig || {};
      const {
        dbTableName,
        dbValueColumn,
        dbWhereColumn,
        dbWhereValue,
        dbHeaderName,
        dbHeaderTemplate,
      } = cfg;

      if (!dbTableName || !dbValueColumn) {
        throw new Error("DB 토큰 설정이 올바르지 않습니다.");
      }

      if (!companyCode) {
        throw new Error("DB 토큰 모드에서는 회사 코드가 필요합니다.");
      }

      const hasWhereColumn = !!dbWhereColumn;
      const hasWhereValue =
        dbWhereValue !== undefined &&
        dbWhereValue !== null &&
        dbWhereValue !== "";

      // where 컬럼/값은 둘 다 비우거나 둘 다 채워야 함
      if (hasWhereColumn !== hasWhereValue) {
        throw new Error(
          "DB 토큰 설정에서 조건 컬럼과 조건 값은 둘 다 비우거나 둘 다 입력해야 합니다."
        );
      }

      // 식별자 검증 (간단한 화이트리스트)
      const identifierRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
      if (
        !identifierRegex.test(dbTableName) ||
        !identifierRegex.test(dbValueColumn) ||
        (hasWhereColumn && !identifierRegex.test(dbWhereColumn as string))
      ) {
        throw new Error(
          "DB 토큰 설정에 유효하지 않은 테이블 또는 컬럼명이 포함되어 있습니다."
        );
      }

      let sql = `
        SELECT ${dbValueColumn} AS token_value
        FROM ${dbTableName}
        WHERE company_code = $1
      `;

      const params: any[] = [companyCode];

      if (hasWhereColumn && hasWhereValue) {
        sql += ` AND ${dbWhereColumn} = $2`;
        params.push(dbWhereValue);
      }

      sql += `
        ORDER BY updated_date DESC
        LIMIT 1
      `;

      const tokenResult: QueryResult<any> = await pool.query(sql, params);

      if (tokenResult.rowCount === 0) {
        throw new Error("DB에서 토큰을 찾을 수 없습니다.");
      }

      const tokenValue = tokenResult.rows[0]["token_value"];
      const headerName = dbHeaderName || "Authorization";
      const template = dbHeaderTemplate || "Bearer {{value}}";

      headers[headerName] = template.replace("{{value}}", tokenValue);
    } else if (authType === "bearer" && authConfig?.token) {
      headers["Authorization"] = `Bearer ${authConfig.token}`;
    } else if (authType === "basic" && authConfig) {
      const credentials = Buffer.from(
        `${authConfig.username}:${authConfig.password}`
      ).toString("base64");
      headers["Authorization"] = `Basic ${credentials}`;
    } else if (authType === "api-key" && authConfig) {
      if (authConfig.keyLocation === "header") {
        headers[authConfig.keyName] = authConfig.keyValue;
      }
    }

    return headers;
  }

  /**
   * REST API 연결 테스트 (테스트 요청 데이터 기반)
   */
  static async testConnection(
    testRequest: RestApiTestRequest,
    userCompanyCode?: string
  ): Promise<RestApiTestResult> {
    const startTime = Date.now();

    try {
      // 헤더 구성
      let headers = { ...testRequest.headers };

      // 인증 헤더 생성 및 병합
      const authHeaders = await this.getAuthHeaders(
        testRequest.auth_type,
        testRequest.auth_config,
        userCompanyCode
      );
      headers = { ...headers, ...authHeaders };

      // URL 구성
      let url = testRequest.base_url;
      if (testRequest.endpoint) {
        url = testRequest.endpoint.startsWith("/")
          ? `${testRequest.base_url}${testRequest.endpoint}`
          : `${testRequest.base_url}/${testRequest.endpoint}`;
      }

      // API Key가 쿼리에 있는 경우
      if (
        testRequest.auth_type === "api-key" &&
        testRequest.auth_config?.keyLocation === "query" &&
        testRequest.auth_config?.keyName &&
        testRequest.auth_config?.keyValue
      ) {
        const separator = url.includes("?") ? "&" : "?";
        url = `${url}${separator}${testRequest.auth_config.keyName}=${testRequest.auth_config.keyValue}`;
      }

      logger.info(
        `REST API 연결 테스트: ${testRequest.method || "GET"} ${url}`
      );

      // Body 처리
      let body: any = undefined;
      if (testRequest.body) {
        // 이미 문자열이면 그대로, 객체면 JSON 문자열로 변환
        if (typeof testRequest.body === "string") {
          body = testRequest.body;
        } else {
          body = JSON.stringify(testRequest.body);
        }

        // Content-Type 헤더가 없으면 기본적으로 application/json 추가
        const hasContentType = Object.keys(headers).some(
          (k) => k.toLowerCase() === "content-type"
        );
        if (!hasContentType) {
          headers["Content-Type"] = "application/json";
        }
      }

      // HTTP 요청 실행
      // [인수인계 중요] 2024-11-27 추가
      // 특정 레거시/내부망 API(예: thiratis.com)의 경우 SSL 인증서 체인 문제로 인해
      // Node.js 레벨에서 검증 실패(UNABLE_TO_VERIFY_LEAF_SIGNATURE)가 발생합니다.
      //
      // 원래는 인프라(OS/Docker)에 루트 CA를 등록하는 것이 정석이나,
      // 유지보수 및 설정 편의성을 위해 코드 레벨에서 '특정 도메인'에 한해서만
      // SSL 검증을 우회하도록 예외 처리를 해두었습니다.
      //
      // ※ 보안 주의: 여기에 모르는 도메인을 함부로 추가하면 중간자 공격(MITM)에 취약해질 수 있습니다.
      // 꼭 필요한 신뢰할 수 있는 도메인만 추가하세요.
      const bypassDomains = ["thiratis.com"];
      const shouldBypassTls = bypassDomains.some((domain) =>
        url.includes(domain)
      );

      const httpsAgent = new https.Agent({
        // bypassDomains에 포함된 URL이면 검증을 무시(false), 아니면 정상 검증(true)
        rejectUnauthorized: !shouldBypassTls,
      });

      const requestConfig = {
        url,
        method: (testRequest.method || "GET") as any,
        headers,
        data: body,
        httpsAgent,
        timeout: testRequest.timeout || 30000,
        // 4xx/5xx 도 예외가 아니라 응답 객체로 처리
        validateStatus: () => true,
      };

      // 요청 상세 로그 (민감 정보는 최소화)
      logger.info(
        `REST API 연결 테스트 요청 상세: ${JSON.stringify({
          method: requestConfig.method,
          url: requestConfig.url,
          headers: {
            ...requestConfig.headers,
            // Authorization 헤더는 마스킹
            Authorization: requestConfig.headers?.Authorization
              ? "***masked***"
              : undefined,
          },
          hasBody: !!body,
        })}`
      );

      const response: AxiosResponse = await axios.request(requestConfig);

      const responseTime = Date.now() - startTime;
      // axios는 response.data에 이미 파싱된 응답 본문을 담아준다.
      // JSON이 아니어도 그대로 내려보내서 프론트에서 확인할 수 있게 한다.
      const responseData = response.data ?? null;

      return {
        success: response.status >= 200 && response.status < 300,
        message:
          response.status >= 200 && response.status < 300
          ? "연결 성공"
          : `연결 실패 (${response.status} ${response.statusText})`,
        response_time: responseTime,
        status_code: response.status,
        response_data: responseData,
      };
    } catch (error) {
      const responseTime = Date.now() - startTime;

      logger.error("REST API 연결 테스트 오류:", error);

      return {
        success: false,
        message: "연결 실패",
        response_time: responseTime,
        error_details:
          error instanceof Error ? error.message : "알 수 없는 오류",
      };
    }
  }

  /**
   * REST API 연결 테스트 (ID 기반)
   */
  static async testConnectionById(
    id: number,
    endpoint?: string
  ): Promise<RestApiTestResult> {
    try {
      const connectionResult = await this.getConnectionById(id);

      if (!connectionResult.success || !connectionResult.data) {
        return {
          success: false,
          message: "연결을 찾을 수 없습니다.",
        };
      }

      const connection = connectionResult.data;

      // 리스트에서 endpoint를 넘기지 않으면,
      // 저장된 endpoint_path를 기본 엔드포인트로 사용
      const effectiveEndpoint =
        endpoint || connection.endpoint_path || undefined;

      const testRequest: RestApiTestRequest = {
        id: connection.id,
        base_url: connection.base_url,
        endpoint: effectiveEndpoint,
        method: (connection.default_method as any) || "GET", // 기본 메서드 적용
        headers: connection.default_headers,
        body: connection.default_body, // 기본 바디 적용
        auth_type: connection.auth_type,
        auth_config: connection.auth_config,
        timeout: connection.timeout,
      };

      const result = await this.testConnection(
        testRequest,
        connection.company_code
      );

      // 테스트 결과 저장
      await pool.query(
        `
        UPDATE external_rest_api_connections
        SET 
          last_test_date = NOW(),
          last_test_result = $1,
          last_test_message = $2
        WHERE id = $3
      `,
        [result.success ? "Y" : "N", result.message, id]
      );

      return result;
    } catch (error) {
      logger.error("REST API 연결 테스트 (ID) 오류:", error);

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

      // 예외가 발생한 경우에도 마지막 테스트 결과를 실패로 기록
      try {
        await pool.query(
          `
          UPDATE external_rest_api_connections
          SET 
            last_test_date = NOW(),
            last_test_result = $1,
            last_test_message = $2
          WHERE id = $3
        `,
          ["N", errorMessage, id]
        );
      } catch (updateError) {
        logger.error(
          "REST API 연결 테스트 (ID) 오류 기록 실패:",
          updateError
        );
      }

      return {
        success: false,
        message: "연결 테스트에 실패했습니다.",
        error_details: errorMessage,
      };
    }
  }

  /**
   * 민감 정보 암호화
   */
  private static encryptSensitiveData(authConfig: any): any {
    if (!authConfig) return null;

    const encrypted = { ...authConfig };

    // 암호화 대상 필드
    if (encrypted.keyValue) {
      encrypted.keyValue = this.encrypt(encrypted.keyValue);
    }
    if (encrypted.token) {
      encrypted.token = this.encrypt(encrypted.token);
    }
    if (encrypted.password) {
      encrypted.password = this.encrypt(encrypted.password);
    }
    if (encrypted.clientSecret) {
      encrypted.clientSecret = this.encrypt(encrypted.clientSecret);
    }

    return encrypted;
  }

  /**
   * 민감 정보 복호화
   */
  private static decryptSensitiveData(authConfig: any): any {
    if (!authConfig) return null;

    const decrypted = { ...authConfig };

    // 복호화 대상 필드
    try {
      if (decrypted.keyValue) {
        decrypted.keyValue = this.decrypt(decrypted.keyValue);
      }
      if (decrypted.token) {
        decrypted.token = this.decrypt(decrypted.token);
      }
      if (decrypted.password) {
        decrypted.password = this.decrypt(decrypted.password);
      }
      if (decrypted.clientSecret) {
        decrypted.clientSecret = this.decrypt(decrypted.clientSecret);
      }
    } catch (error) {
      logger.warn("민감 정보 복호화 실패 (암호화되지 않은 데이터일 수 있음)");
    }

    return decrypted;
  }

  /**
   * 암호화 헬퍼
   */
  private static encrypt(text: string): string {
    const iv = crypto.randomBytes(16);
    const key = crypto.scryptSync(ENCRYPTION_KEY, "salt", 32);
    const cipher = crypto.createCipheriv(ALGORITHM, key, iv);

    let encrypted = cipher.update(text, "utf8", "hex");
    encrypted += cipher.final("hex");

    const authTag = cipher.getAuthTag();

    return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}`;
  }

  /**
   * 복호화 헬퍼
   */
  private static decrypt(text: string): string {
    const parts = text.split(":");
    if (parts.length !== 3) {
      // 암호화되지 않은 데이터
      return text;
    }

    const iv = Buffer.from(parts[0], "hex");
    const authTag = Buffer.from(parts[1], "hex");
    const encryptedText = parts[2];

    const key = crypto.scryptSync(ENCRYPTION_KEY, "salt", 32);
    const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
    decipher.setAuthTag(authTag);

    let decrypted = decipher.update(encryptedText, "hex", "utf8");
    decrypted += decipher.final("utf8");

    return decrypted;
  }

  /**
   * REST API 데이터 조회 (화면관리용 프록시)
   * 저장된 연결 정보를 사용하여 외부 REST API를 호출하고 데이터를 반환
   */
  static async fetchData(
    connectionId: number,
    endpoint?: string,
    jsonPath?: string,
    userCompanyCode?: string
  ): Promise<ApiResponse<any>> {
    try {
      // 연결 정보 조회
      const connectionResult = await this.getConnectionById(connectionId, userCompanyCode);

      if (!connectionResult.success || !connectionResult.data) {
        return {
          success: false,
          message: "REST API 연결을 찾을 수 없습니다.",
          error: {
            code: "CONNECTION_NOT_FOUND",
            details: `연결 ID ${connectionId}를 찾을 수 없습니다.`,
          },
        };
      }

      const connection = connectionResult.data;

      // 비활성화된 연결인지 확인
      if (connection.is_active !== "Y") {
        return {
          success: false,
          message: "비활성화된 REST API 연결입니다.",
          error: {
            code: "CONNECTION_INACTIVE",
            details: "연결이 비활성화 상태입니다.",
          },
        };
      }

      // 엔드포인트 결정 (파라미터 > 저장된 값)
      const effectiveEndpoint = endpoint || connection.endpoint_path || "";

      // API 호출을 위한 테스트 요청 생성
      const testRequest: RestApiTestRequest = {
        id: connection.id,
        base_url: connection.base_url,
        endpoint: effectiveEndpoint,
        method: (connection.default_method as any) || "GET",
        headers: connection.default_headers,
        body: connection.default_body,
        auth_type: connection.auth_type,
        auth_config: connection.auth_config,
        timeout: connection.timeout,
      };

      // API 호출
      const result = await this.testConnection(testRequest, connection.company_code);

      if (!result.success) {
        return {
          success: false,
          message: result.message || "REST API 호출에 실패했습니다.",
          error: {
            code: "API_CALL_FAILED",
            details: result.error_details,
          },
        };
      }

      // 응답 데이터에서 jsonPath로 데이터 추출
      let extractedData = result.response_data;

      logger.info(`REST API 원본 응답 데이터 타입: ${typeof result.response_data}`);
      logger.info(`REST API 원본 응답 데이터 (일부): ${JSON.stringify(result.response_data)?.substring(0, 500)}`);

      if (jsonPath && result.response_data) {
        try {
          // jsonPath로 데이터 추출 (예: "data", "data.items", "result.list")
          const pathParts = jsonPath.split(".");
          logger.info(`JSON Path 파싱: ${jsonPath} -> [${pathParts.join(", ")}]`);
          
          for (const part of pathParts) {
            if (extractedData && typeof extractedData === "object") {
              extractedData = (extractedData as any)[part];
              logger.info(`JSON Path '${part}' 추출 결과 타입: ${typeof extractedData}, 배열?: ${Array.isArray(extractedData)}`);
            } else {
              logger.warn(`JSON Path '${part}' 추출 실패: extractedData가 객체가 아님`);
              break;
            }
          }
        } catch (pathError) {
          logger.warn(`JSON Path 추출 실패: ${jsonPath}`, pathError);
          // 추출 실패 시 원본 데이터 반환
          extractedData = result.response_data;
        }
      }

      // 데이터가 배열이 아닌 경우 배열로 변환
      // null이나 undefined인 경우 빈 배열로 처리
      let dataArray: any[] = [];
      if (extractedData === null || extractedData === undefined) {
        logger.warn("추출된 데이터가 null/undefined입니다. 원본 응답 데이터를 사용합니다.");
        // jsonPath 추출 실패 시 원본 데이터에서 직접 컬럼 추출 시도
        if (result.response_data && typeof result.response_data === "object") {
          dataArray = Array.isArray(result.response_data) ? result.response_data : [result.response_data];
        }
      } else {
        dataArray = Array.isArray(extractedData) ? extractedData : [extractedData];
      }

      logger.info(`최종 데이터 배열 길이: ${dataArray.length}`);
      if (dataArray.length > 0) {
        logger.info(`첫 번째 데이터 항목: ${JSON.stringify(dataArray[0])?.substring(0, 300)}`);
      }

      // 컬럼 정보 추출 (첫 번째 유효한 데이터 기준)
      let columns: Array<{ columnName: string; columnLabel: string; dataType: string }> = [];
      
      // 첫 번째 유효한 객체 찾기
      const firstValidItem = dataArray.find(item => item && typeof item === "object" && !Array.isArray(item));
      
      if (firstValidItem) {
        columns = Object.keys(firstValidItem).map((key) => ({
          columnName: key,
          columnLabel: key,
          dataType: typeof firstValidItem[key],
        }));
        logger.info(`추출된 컬럼 수: ${columns.length}, 컬럼명: [${columns.map(c => c.columnName).join(", ")}]`);
      } else {
        logger.warn("유효한 데이터 항목을 찾을 수 없어 컬럼을 추출할 수 없습니다.");
      }

      return {
        success: true,
        data: {
          rows: dataArray,
          columns,
          total: dataArray.length,
          connectionInfo: {
            connectionId: connection.id,
            connectionName: connection.connection_name,
            baseUrl: connection.base_url,
            endpoint: effectiveEndpoint,
          },
        },
        message: `${dataArray.length}개의 데이터를 조회했습니다.`,
      };
    } catch (error) {
      logger.error("REST API 데이터 조회 오류:", error);
      return {
        success: false,
        message: "REST API 데이터 조회에 실패했습니다.",
        error: {
          code: "FETCH_ERROR",
          details: error instanceof Error ? error.message : "알 수 없는 오류",
        },
      };
    }
  }

  /**
   * 연결 데이터 유효성 검증
   */
  private static validateConnectionData(data: ExternalRestApiConnection): void {
    if (!data.connection_name || data.connection_name.trim() === "") {
      throw new Error("연결명은 필수입니다.");
    }

    if (!data.base_url || data.base_url.trim() === "") {
      throw new Error("기본 URL은 필수입니다.");
    }

    // URL 형식 검증
    try {
      new URL(data.base_url);
    } catch {
      throw new Error("올바른 URL 형식이 아닙니다.");
    }

    // 인증 타입 검증
    const validAuthTypes: AuthType[] = [
      "none",
      "api-key",
      "bearer",
      "basic",
      "oauth2",
      "db-token",
    ];
    if (!validAuthTypes.includes(data.auth_type)) {
      throw new Error("올바르지 않은 인증 타입입니다.");
    }
  }

  /**
   * 다중 REST API 데이터 조회 및 병합
   * 여러 REST API의 응답을 병합하여 하나의 데이터셋으로 반환
   */
  static async fetchMultipleData(
    configs: Array<{
      connectionId: number;
      endpoint: string;
      jsonPath: string;
      alias: string;
    }>,
    userCompanyCode?: string
  ): Promise<ApiResponse<{
    rows: any[];
    columns: Array<{ columnName: string; columnLabel: string; dataType: string; sourceApi: string }>;
    total: number;
    sources: Array<{ connectionId: number; connectionName: string; rowCount: number }>;
  }>> {
    try {
      logger.info(`다중 REST API 데이터 조회 시작: ${configs.length}개 API`);
      
      // 각 API에서 데이터 조회
      const results = await Promise.all(
        configs.map(async (config) => {
          try {
            const result = await this.fetchData(
              config.connectionId,
              config.endpoint,
              config.jsonPath,
              userCompanyCode
            );
            
            if (result.success && result.data) {
              return {
                success: true,
                connectionId: config.connectionId,
                connectionName: result.data.connectionInfo.connectionName,
                alias: config.alias,
                rows: result.data.rows,
                columns: result.data.columns,
              };
            } else {
              logger.warn(`API ${config.connectionId} 조회 실패:`, result.message);
              return {
                success: false,
                connectionId: config.connectionId,
                connectionName: "",
                alias: config.alias,
                rows: [],
                columns: [],
                error: result.message,
              };
            }
          } catch (error) {
            logger.error(`API ${config.connectionId} 조회 오류:`, error);
            return {
              success: false,
              connectionId: config.connectionId,
              connectionName: "",
              alias: config.alias,
              rows: [],
              columns: [],
              error: error instanceof Error ? error.message : "알 수 없는 오류",
            };
          }
        })
      );

      // 성공한 결과만 필터링
      const successfulResults = results.filter(r => r.success);
      
      if (successfulResults.length === 0) {
        return {
          success: false,
          message: "모든 REST API 조회에 실패했습니다.",
          error: {
            code: "ALL_APIS_FAILED",
            details: results.map(r => ({ connectionId: r.connectionId, error: r.error })),
          },
        };
      }

      // 컬럼 병합 (별칭 적용)
      const mergedColumns: Array<{ columnName: string; columnLabel: string; dataType: string; sourceApi: string }> = [];
      
      for (const result of successfulResults) {
        for (const col of result.columns) {
          const prefixedColumnName = result.alias ? `${result.alias}${col.columnName}` : col.columnName;
          mergedColumns.push({
            columnName: prefixedColumnName,
            columnLabel: `${col.columnLabel} (${result.connectionName})`,
            dataType: col.dataType,
            sourceApi: result.connectionName,
          });
        }
      }

      // 데이터 병합 (가로 병합: 각 API의 첫 번째 행끼리 병합)
      // 참고: 실제 사용 시에는 조인 키가 필요할 수 있음
      const maxRows = Math.max(...successfulResults.map(r => r.rows.length));
      const mergedRows: any[] = [];

      for (let i = 0; i < maxRows; i++) {
        const mergedRow: any = {};
        
        for (const result of successfulResults) {
          const row = result.rows[i] || {};
          
          for (const [key, value] of Object.entries(row)) {
            const prefixedKey = result.alias ? `${result.alias}${key}` : key;
            mergedRow[prefixedKey] = value;
          }
        }
        
        mergedRows.push(mergedRow);
      }

      logger.info(`다중 REST API 데이터 병합 완료: ${mergedRows.length}개 행, ${mergedColumns.length}개 컬럼`);

      return {
        success: true,
        data: {
          rows: mergedRows,
          columns: mergedColumns,
          total: mergedRows.length,
          sources: successfulResults.map(r => ({
            connectionId: r.connectionId,
            connectionName: r.connectionName,
            rowCount: r.rows.length,
          })),
        },
        message: `${successfulResults.length}개 API에서 총 ${mergedRows.length}개 데이터를 조회했습니다.`,
      };
    } catch (error) {
      logger.error("다중 REST API 데이터 조회 오류:", error);
      return {
        success: false,
        message: "다중 REST API 데이터 조회에 실패했습니다.",
        error: {
          code: "MULTI_FETCH_ERROR",
          details: error instanceof Error ? error.message : "알 수 없는 오류",
        },
      };
    }
  }
}
