import axios, { AxiosInstance, AxiosResponse } from "axios";
import https from "https";
import {
  DatabaseConnector,
  ConnectionConfig,
  QueryResult,
} from "../interfaces/DatabaseConnector";
import { ConnectionTestResult, TableInfo } from "../types/externalDbTypes";

export interface RestApiConfig {
  baseUrl: string;
  apiKey: string;
  timeout?: number;
  // ConnectionConfig 호환성을 위한 더미 필드들 (사용하지 않음)
  host?: string;
  port?: number;
  database?: string;
  user?: string;
  password?: string;
}

export class RestApiConnector implements DatabaseConnector {
  private httpClient: AxiosInstance;
  private config: RestApiConfig;

  constructor(config: RestApiConfig) {
    this.config = config;
    // Axios 인스턴스 생성
    // 🔐 apiKey가 없을 수도 있으므로 Authorization 헤더는 선택적으로만 추가
    const defaultHeaders: Record<string, string> = {
      "Content-Type": "application/json",
      Accept: "application/json",
    };

    if (config.apiKey) {
      defaultHeaders["Authorization"] = `Bearer ${config.apiKey}`;
    }

    this.httpClient = axios.create({
      baseURL: config.baseUrl,
      timeout: config.timeout || 30000,
      headers: defaultHeaders,
      // ⚠️ 외부 API 중 자체 서명 인증서를 사용하는 경우가 있어서
      // 인증서 검증을 끈 HTTPS 에이전트를 사용한다.
      // 내부망/신뢰된 시스템 전용으로 사용해야 하며,
      // 공개 인터넷용 API에는 적용하면 안 된다.
      httpsAgent: new https.Agent({ rejectUnauthorized: false }),
    });

    // 요청/응답 인터셉터 설정
    this.setupInterceptors();
  }

  private setupInterceptors() {
    // 요청 인터셉터
    this.httpClient.interceptors.request.use(
      (config) => {
        console.log(
          `[RestApiConnector] 요청: ${config.method?.toUpperCase()} ${config.url}`
        );
        return config;
      },
      (error) => {
        console.error("[RestApiConnector] 요청 오류:", error);
        return Promise.reject(error);
      }
    );

    // 응답 인터셉터
    this.httpClient.interceptors.response.use(
      (response) => {
        console.log(
          `[RestApiConnector] 응답: ${response.status} ${response.statusText}`
        );
        return response;
      },
      (error) => {
        console.error(
          "[RestApiConnector] 응답 오류:",
          error.response?.status,
          error.response?.statusText
        );
        return Promise.reject(error);
      }
    );
  }

  async connect(): Promise<void> {
    // 기존에는 /health 엔드포인트를 호출해서 미리 연결을 검사했지만,
    // 일반 외부 API들은 /health가 없거나 401/500을 반환하는 경우가 많아
    // 불필요하게 예외가 나면서 미리보기/배치 실행이 막히는 문제가 있었다.
    //
    // 따라서 여기서는 "연결 준비 완료" 정도만 로그로 남기고
    // 실제 호출 실패 여부는 executeRequest 단계에서만 판단하도록 한다.
    console.log(
      `[RestApiConnector] 연결 준비 완료 (사전 헬스체크 생략): ${this.config.baseUrl}`
    );
    return;
  }

  async disconnect(): Promise<void> {
    // REST API는 연결 해제가 필요 없음
    console.log(`[RestApiConnector] 연결 해제: ${this.config.baseUrl}`);
  }

  async testConnection(): Promise<ConnectionTestResult> {
    try {
      await this.connect();
      return {
        success: true,
        message: "REST API 연결이 성공했습니다.",
        details: {
          response_time: Date.now(),
        },
      };
    } catch (error) {
      return {
        success: false,
        message:
          error instanceof Error
            ? error.message
            : "REST API 연결에 실패했습니다.",
        details: {
          response_time: Date.now(),
        },
      };
    }
  }

  // 🔥 DatabaseConnector 인터페이스 호환용 executeQuery (사용하지 않음)
  async executeQuery(query: string, params?: any[]): Promise<QueryResult> {
    // REST API는 executeRequest를 사용해야 함
    throw new Error(
      "REST API Connector는 executeQuery를 지원하지 않습니다. executeRequest를 사용하세요."
    );
  }

  // 🔥 실제 REST API 요청을 위한 메서드
  async executeRequest(
    endpoint: string,
    method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
    data?: any
  ): Promise<QueryResult> {
    try {
      const startTime = Date.now();
      let response: AxiosResponse;

      // HTTP 메서드에 따른 요청 실행
      switch (method.toUpperCase()) {
        case "GET":
          response = await this.httpClient.get(endpoint);
          break;
        case "POST":
          response = await this.httpClient.post(endpoint, data);
          break;
        case "PUT":
          response = await this.httpClient.put(endpoint, data);
          break;
        case "DELETE":
          response = await this.httpClient.delete(endpoint);
          break;
        default:
          throw new Error(`지원하지 않는 HTTP 메서드: ${method}`);
      }

      const executionTime = Date.now() - startTime;
      const responseData = response.data;

      console.log(`[RestApiConnector] 원본 응답 데이터:`, {
        type: typeof responseData,
        isArray: Array.isArray(responseData),
        keys:
          typeof responseData === "object"
            ? Object.keys(responseData)
            : "not object",
        responseData: responseData,
      });

      // 응답 데이터 처리
      let rows: any[];
      if (Array.isArray(responseData)) {
        rows = responseData;
      } else if (
        responseData &&
        responseData.data &&
        Array.isArray(responseData.data)
      ) {
        // API 응답이 {success: true, data: [...]} 형태인 경우
        rows = responseData.data;
      } else if (
        responseData &&
        responseData.data &&
        typeof responseData.data === "object"
      ) {
        // API 응답이 {success: true, data: {...}} 형태인 경우 (단일 객체)
        rows = [responseData.data];
      } else if (
        responseData &&
        typeof responseData === "object" &&
        !Array.isArray(responseData)
      ) {
        // 단일 객체 응답인 경우
        rows = [responseData];
      } else {
        rows = [];
      }

      console.log(`[RestApiConnector] 처리된 rows:`, {
        rowsLength: rows.length,
        firstRow: rows.length > 0 ? rows[0] : "no data",
        allRows: rows,
      });

      console.log(`[RestApiConnector] API 호출 결과:`, {
        endpoint,
        method,
        status: response.status,
        rowCount: rows.length,
        executionTime: `${executionTime}ms`,
      });

      return {
        rows: rows,
        rowCount: rows.length,
        fields:
          rows.length > 0
            ? Object.keys(rows[0]).map((key) => ({ name: key, type: "string" }))
            : [],
      };
    } catch (error) {
      console.error(
        `[RestApiConnector] API 호출 오류 (${method} ${endpoint}):`,
        error
      );

      if (axios.isAxiosError(error)) {
        throw new Error(
          `REST API 호출 실패: ${error.response?.status} ${error.response?.statusText}`
        );
      }

      throw new Error(
        `REST API 호출 실패: ${error instanceof Error ? error.message : "알 수 없는 오류"}`
      );
    }
  }

  async getTables(): Promise<TableInfo[]> {
    // REST API의 경우 "테이블"은 사용 가능한 엔드포인트를 의미
    // 일반적인 REST API 엔드포인트들을 반환
    return [
      {
        table_name: "/api/users",
        columns: [],
        description: "사용자 정보 API",
      },
      {
        table_name: "/api/data",
        columns: [],
        description: "기본 데이터 API",
      },
      {
        table_name: "/api/custom",
        columns: [],
        description: "사용자 정의 엔드포인트",
      },
    ];
  }

  async getTableList(): Promise<TableInfo[]> {
    return this.getTables();
  }

  async getColumns(endpoint: string): Promise<any[]> {
    try {
      // GET 요청으로 샘플 데이터를 가져와서 필드 구조 파악
      const result = await this.executeRequest(endpoint, "GET");

      if (result.rows.length > 0) {
        const sampleRow = result.rows[0];
        return Object.keys(sampleRow).map((key) => ({
          column_name: key,
          data_type: typeof sampleRow[key],
          is_nullable: "YES",
          column_default: null,
          description: `${key} 필드`,
        }));
      }

      return [];
    } catch (error) {
      console.error(
        `[RestApiConnector] 컬럼 정보 조회 오류 (${endpoint}):`,
        error
      );
      return [];
    }
  }

  async getTableColumns(endpoint: string): Promise<any[]> {
    return this.getColumns(endpoint);
  }

  // REST API 전용 메서드들
  async getData(
    endpoint: string,
    params?: Record<string, any>
  ): Promise<any[]> {
    const queryString = params
      ? "?" + new URLSearchParams(params).toString()
      : "";
    const result = await this.executeRequest(endpoint + queryString, "GET");
    return result.rows;
  }

  async postData(endpoint: string, data: any): Promise<any> {
    const result = await this.executeRequest(endpoint, "POST", data);
    return result.rows[0];
  }

  async putData(endpoint: string, data: any): Promise<any> {
    const result = await this.executeRequest(endpoint, "PUT", data);
    return result.rows[0];
  }

  async deleteData(endpoint: string): Promise<any> {
    const result = await this.executeRequest(endpoint, "DELETE");
    return result.rows[0];
  }
}
