import { Client } from "pg";
import {
  DatabaseConnector,
  ConnectionConfig,
  QueryResult,
} from "../interfaces/DatabaseConnector";
import { ConnectionTestResult, TableInfo } from "../types/externalDbTypes";

export class PostgreSQLConnector implements DatabaseConnector {
  private client: Client | null = null;
  private config: ConnectionConfig;

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

  async connect(): Promise<void> {
    // 기존 연결이 있다면 먼저 정리
    await this.forceDisconnect();

    const clientConfig: any = {
      host: this.config.host,
      port: this.config.port,
      database: this.config.database,
      user: this.config.user,
      password: this.config.password,
      // 연결 안정성 개선 (더 보수적인 설정)
      connectionTimeoutMillis: this.config.connectionTimeoutMillis || 15000,
      query_timeout: this.config.queryTimeoutMillis || 20000,
      keepAlive: false, // keepAlive 비활성화 (연결 문제 방지)
      // SASL 인증 문제 방지
      application_name: "PLM-ERP-System",
      // 추가 안정성 설정
      statement_timeout: 20000,
      idle_in_transaction_session_timeout: 30000,
    };

    if (this.config.ssl != null) {
      clientConfig.ssl = this.config.ssl;
    }

    this.client = new Client(clientConfig);

    // 연결 시 더 긴 타임아웃 설정
    const connectPromise = this.client.connect();
    const timeoutPromise = new Promise((_, reject) => {
      setTimeout(() => reject(new Error("연결 타임아웃")), 20000);
    });

    await Promise.race([connectPromise, timeoutPromise]);
    console.log(
      `✅ PostgreSQL 연결 성공: ${this.config.host}:${this.config.port}`
    );
  }

  // 강제 연결 해제 메서드 추가
  private async forceDisconnect(): Promise<void> {
    if (this.client) {
      try {
        await this.client.end();
      } catch (error) {
        console.warn("강제 연결 해제 중 오류 (무시):", error);
      } finally {
        this.client = null;
      }
    }
  }

  async disconnect(): Promise<void> {
    if (this.client) {
      try {
        const endPromise = this.client.end();
        const timeoutPromise = new Promise((_, reject) => {
          setTimeout(() => reject(new Error("연결 해제 타임아웃")), 3000);
        });

        await Promise.race([endPromise, timeoutPromise]);
        console.log(`✅ PostgreSQL 연결 해제 성공`);
      } catch (error) {
        console.warn("연결 해제 중 오류:", error);
      } finally {
        this.client = null;
      }
    }
  }

  async testConnection(): Promise<ConnectionTestResult> {
    const startTime = Date.now();
    try {
      await this.connect();
      const result = await this.client!.query(
        "SELECT version(), pg_database_size(current_database()) as size"
      );
      const responseTime = Date.now() - startTime;
      await this.disconnect();
      return {
        success: true,
        message: "PostgreSQL 연결이 성공했습니다.",
        details: {
          response_time: responseTime,
          server_version: result.rows[0]?.version || "알 수 없음",
          database_size: this.formatBytes(
            parseInt(result.rows[0]?.size || "0")
          ),
        },
      };
    } catch (error: any) {
      await this.disconnect();
      return {
        success: false,
        message: "PostgreSQL 연결에 실패했습니다.",
        error: {
          code: "CONNECTION_FAILED",
          details: error.message || "알 수 없는 오류",
        },
      };
    }
  }

  async executeQuery(query: string, params: any[] = []): Promise<QueryResult> {
    try {
      await this.connect();
      const result = await this.client!.query(query, params);
      await this.disconnect();
      return {
        rows: result.rows,
        rowCount: result.rowCount ?? undefined,
        fields: result.fields ?? undefined,
      };
    } catch (error: any) {
      await this.disconnect();
      throw new Error(`PostgreSQL 쿼리 실행 실패: ${error.message}`);
    }
  }

  async getTables(): Promise<TableInfo[]> {
    let tempClient: Client | null = null;
    try {
      console.log(
        `🔍 PostgreSQL 테이블 목록 조회 시작: ${this.config.host}:${this.config.port}`
      );

      // 매번 새로운 연결 생성
      const clientConfig: any = {
        host: this.config.host,
        port: this.config.port,
        database: this.config.database,
        user: this.config.user,
        password: this.config.password,
        connectionTimeoutMillis: 10000,
        query_timeout: 15000,
        application_name: "PLM-ERP-Tables",
      };

      tempClient = new Client(clientConfig);
      await tempClient.connect();

      const result = await tempClient.query(`
        SELECT
          t.table_name,
          obj_description(quote_ident(t.table_name)::regclass::oid, 'pg_class') as table_description
        FROM information_schema.tables t
        WHERE t.table_schema = 'public'
          AND t.table_type = 'BASE TABLE'
        ORDER BY t.table_name;
      `);

      console.log(`✅ 테이블 목록 조회 성공: ${result.rows.length}개`);
      return result.rows.map((row) => ({
        table_name: row.table_name,
        description: row.table_description,
        columns: [], // Columns will be fetched by getColumns
      }));
    } catch (error: any) {
      console.error(`❌ 테이블 목록 조회 실패:`, error.message);
      throw new Error(`PostgreSQL 테이블 목록 조회 실패: ${error.message}`);
    } finally {
      if (tempClient) {
        try {
          await tempClient.end();
        } catch (endError) {
          console.warn("테이블 조회 연결 해제 중 오류:", endError);
        }
      }
    }
  }

  async getColumns(tableName: string): Promise<any[]> {
    let tempClient: Client | null = null;
    try {
      console.log(
        `🔍 PostgreSQL 컬럼 정보 조회 시작: ${this.config.host}:${this.config.port}/${tableName}`
      );

      // 매번 새로운 연결 생성
      const clientConfig: any = {
        host: this.config.host,
        port: this.config.port,
        database: this.config.database,
        user: this.config.user,
        password: this.config.password,
        connectionTimeoutMillis: 10000,
        query_timeout: 15000,
        application_name: "PLM-ERP-Columns",
      };

      tempClient = new Client(clientConfig);
      await tempClient.connect();

      const result = await tempClient.query(
        `
        SELECT
          isc.column_name,
          isc.data_type,
          isc.is_nullable,
          isc.column_default,
          col_description(c.oid, a.attnum) as column_comment,
          CASE 
            WHEN tc.constraint_type = 'PRIMARY KEY' THEN 'YES'
            ELSE 'NO'
          END AS is_primary_key
        FROM information_schema.columns isc
        LEFT JOIN pg_class c 
          ON c.relname = isc.table_name
             AND c.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = isc.table_schema)
        LEFT JOIN pg_attribute a 
          ON a.attrelid = c.oid 
         AND a.attname = isc.column_name
        LEFT JOIN information_schema.key_column_usage k
          ON k.table_name = isc.table_name
         AND k.table_schema = isc.table_schema
         AND k.column_name = isc.column_name
        LEFT JOIN information_schema.table_constraints tc
          ON tc.constraint_name = k.constraint_name
         AND tc.table_schema    = k.table_schema
         AND tc.table_name      = k.table_name
         AND tc.constraint_type = 'PRIMARY KEY'
        WHERE isc.table_schema = 'public' 
          AND isc.table_name   = $1
        ORDER BY isc.ordinal_position;
      `,
        [tableName]
      );

      console.log(
        `✅ 컬럼 정보 조회 성공: ${tableName} - ${result.rows.length}개`
      );
      return result.rows;
    } catch (error: any) {
      console.error(`❌ 컬럼 정보 조회 실패: ${tableName} -`, error.message);
      throw new Error(`PostgreSQL 컬럼 정보 조회 실패: ${error.message}`);
    } finally {
      if (tempClient) {
        try {
          await tempClient.end();
        } catch (endError) {
          console.warn("컬럼 조회 연결 해제 중 오류:", endError);
        }
      }
    }
  }

  private formatBytes(bytes: number): string {
    if (bytes === 0) return "0 Bytes";
    const k = 1024;
    const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
  }
}
