// @ts-ignore
import * as oracledb from "oracledb";
import {
  DatabaseConnector,
  ConnectionConfig,
  QueryResult,
} from "../interfaces/DatabaseConnector";
import { ConnectionTestResult, TableInfo } from "../types/externalDbTypes";

export class OracleConnector implements DatabaseConnector {
  private connection: oracledb.Connection | null = null;
  private config: ConnectionConfig;

  constructor(config: ConnectionConfig) {
    this.config = config;

    // Oracle XE 21c 특화 설정
    // oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
    // oracledb.autoCommit = true;
  }

  async connect(): Promise<void> {
    try {
      // Oracle XE 21c 연결 문자열 구성
      const connectionString = this.buildConnectionString();

      const connectionConfig: any = {
        user: this.config.user,
        password: this.config.password,
        connectString: connectionString,
      };

      this.connection = await oracledb.getConnection(connectionConfig);
      console.log("Oracle XE 21c 연결 성공");
    } catch (error: any) {
      console.error("Oracle XE 21c 연결 실패:", error);
      throw new Error(`Oracle 연결 실패: ${error.message}`);
    }
  }

  private buildConnectionString(): string {
    const { host, port, database } = this.config;

    // Oracle XE 21c는 기본적으로 XE 서비스명을 사용
    // 다양한 연결 문자열 형식 지원
    if (database.includes("/") || database.includes(":")) {
      // 이미 완전한 연결 문자열인 경우
      return database;
    }

    // Oracle XE 21c 표준 형식
    return `${host}:${port}/${database}`;
  }

  async disconnect(): Promise<void> {
    if (this.connection) {
      try {
        await this.connection.close();
        this.connection = null;
        console.log("Oracle 연결 해제됨");
      } catch (error: any) {
        console.error("Oracle 연결 해제 실패:", error);
      }
    }
  }

  async testConnection(): Promise<ConnectionTestResult> {
    try {
      if (!this.connection) {
        await this.connect();
      }

      // Oracle XE 21c 버전 확인 쿼리
      const result = await this.connection!.execute(
        "SELECT BANNER FROM V$VERSION WHERE BANNER LIKE 'Oracle%'"
      );

      console.log("Oracle 버전:", result.rows);
      return {
        success: true,
        message: "연결 성공",
        details: {
          server_version: (result.rows as any)?.[0]?.BANNER || "Unknown",
        },
      };
    } catch (error: any) {
      console.error("Oracle 연결 테스트 실패:", error);
      return {
        success: false,
        message: "연결 실패",
        details: {
          server_version: error.message,
        },
      };
    }
  }

  async executeQuery(query: string, params: any[] = []): Promise<QueryResult> {
    if (!this.connection) {
      await this.connect();
    }

    try {
      const startTime = Date.now();

      // 쿼리 타입 확인
      const isDML = /^\s*(INSERT|UPDATE|DELETE|MERGE)/i.test(query);
      const isCOMMIT = /^\s*COMMIT/i.test(query);
      const isROLLBACK = /^\s*ROLLBACK/i.test(query);

      // 🔥 COMMIT/ROLLBACK 명령은 직접 실행
      if (isCOMMIT || isROLLBACK) {
        if (isCOMMIT) {
          await (this.connection as any).commit();
          console.log("✅ Oracle COMMIT 실행됨");
        } else {
          await (this.connection as any).rollback();
          console.log("⚠️ Oracle ROLLBACK 실행됨");
        }
        return {
          rows: [],
          rowCount: 0,
          fields: [],
          affectedRows: 0,
        };
      }

      // Oracle XE 21c 쿼리 실행 옵션
      const options: any = {
        outFormat: (oracledb as any).OUT_FORMAT_OBJECT, // OBJECT format
        maxRows: 10000, // XE 제한 고려
        fetchArraySize: 100,
        autoCommit: false, // 🔥 수동으로 COMMIT 제어하도록 변경
      };

      console.log("Oracle 쿼리 실행:", {
        query: query.substring(0, 100) + "...",
        isDML,
        autoCommit: options.autoCommit,
      });

      const result = await this.connection!.execute(query, params, options);
      const executionTime = Date.now() - startTime;

      console.log("Oracle 쿼리 실행 결과:", {
        query,
        rowCount: result.rows?.length || 0,
        rowsAffected: result.rowsAffected,
        metaData: result.metaData?.length || 0,
        executionTime: `${executionTime}ms`,
        actualRows: result.rows,
        metaDataInfo: result.metaData,
        autoCommit: options.autoCommit,
      });

      return {
        rows: result.rows || [],
        rowCount: result.rowsAffected || result.rows?.length || 0,
        fields: this.extractFieldInfo(result.metaData || []),
      };
    } catch (error: any) {
      console.error("Oracle 쿼리 실행 실패:", error);
      throw new Error(`쿼리 실행 실패: ${error.message}`);
    }
  }

  private extractFieldInfo(metaData: any[]): any[] {
    return metaData.map((field) => ({
      name: field.name,
      type: this.mapOracleType(field.dbType),
      length: field.precision || field.byteSize,
      nullable: field.nullable,
    }));
  }

  private mapOracleType(oracleType: any): string {
    // Oracle XE 21c 타입 매핑 (간단한 방식)
    if (typeof oracleType === "string") {
      return oracleType;
    }
    return "UNKNOWN";
  }

  async getTables(): Promise<TableInfo[]> {
    try {
      // 현재 사용자 스키마의 테이블들만 조회
      const query = `
        SELECT table_name, USER as owner
        FROM user_tables
        ORDER BY table_name
      `;

      console.log("Oracle 테이블 조회 시작 - 사용자:", this.config.user);

      const result = await this.executeQuery(query);
      console.log("사용자 스키마 테이블 조회 결과:", result.rows);

      const tables = result.rows.map((row: any) => ({
        table_name: row.TABLE_NAME,
        columns: [],
        description: null,
      }));

      console.log(`총 ${tables.length}개의 사용자 테이블을 찾았습니다.`);
      return tables;
    } catch (error: any) {
      console.error("Oracle 테이블 목록 조회 실패:", error);
      throw new Error(`테이블 목록 조회 실패: ${error.message}`);
    }
  }

  async getColumns(tableName: string): Promise<any[]> {
    try {
      console.log(`[OracleConnector] getColumns 호출: tableName=${tableName}`);

      const query = `
        SELECT 
          column_name,
          data_type,
          data_length,
          data_precision,
          data_scale,
          nullable,
          data_default
        FROM user_tab_columns 
        WHERE table_name = UPPER(:tableName)
        ORDER BY column_id
      `;

      console.log(`[OracleConnector] 쿼리 실행 시작: ${query}`);
      const result = await this.executeQuery(query, [tableName]);

      console.log(`[OracleConnector] 쿼리 결과:`, result.rows);
      console.log(
        `[OracleConnector] 결과 개수:`,
        result.rows ? result.rows.length : "null/undefined"
      );

      const mappedResult = result.rows.map((row: any) => ({
        column_name: row.COLUMN_NAME,
        data_type: this.formatOracleDataType(row),
        is_nullable: row.NULLABLE === "Y" ? "YES" : "NO",
        column_default: row.DATA_DEFAULT,
      }));

      console.log(`[OracleConnector] 매핑된 결과:`, mappedResult);
      return mappedResult;
    } catch (error: any) {
      console.error("[OracleConnector] getColumns 오류:", error);
      throw new Error(`테이블 컬럼 조회 실패: ${error.message}`);
    }
  }

  private formatOracleDataType(row: any): string {
    const { DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE } = row;

    switch (DATA_TYPE) {
      case "NUMBER":
        if (DATA_PRECISION && DATA_SCALE !== null) {
          return `NUMBER(${DATA_PRECISION},${DATA_SCALE})`;
        } else if (DATA_PRECISION) {
          return `NUMBER(${DATA_PRECISION})`;
        }
        return "NUMBER";
      case "VARCHAR2":
      case "CHAR":
        return `${DATA_TYPE}(${DATA_LENGTH})`;
      default:
        return DATA_TYPE;
    }
  }
}
