import db from "../database/db";
import {
  FlowExternalDbConnection,
  CreateFlowExternalDbConnectionRequest,
  UpdateFlowExternalDbConnectionRequest,
} from "../types/flow";
import { CredentialEncryption } from "../utils/credentialEncryption";
import { Pool } from "pg";
// import mysql from 'mysql2/promise'; // MySQL용 (추후)
// import { ConnectionPool } from 'mssql'; // MSSQL용 (추후)

/**
 * 플로우 전용 외부 DB 연결 관리 서비스
 * (기존 제어관리 외부 DB 연결과 별도)
 */
export class FlowExternalDbConnectionService {
  private encryption: CredentialEncryption;
  private connectionPools: Map<number, Pool> = new Map();

  constructor() {
    // 환경 변수에서 SECRET_KEY를 가져오거나 기본값 설정
    const secretKey =
      process.env.SECRET_KEY || "flow-external-db-secret-key-2025";
    this.encryption = new CredentialEncryption(secretKey);
  }

  /**
   * 외부 DB 연결 생성
   */
  async create(
    request: CreateFlowExternalDbConnectionRequest,
    userId: string = "system"
  ): Promise<FlowExternalDbConnection> {
    // 비밀번호 암호화
    const encryptedPassword = this.encryption.encrypt(request.password);

    const query = `
      INSERT INTO flow_external_db_connection (
        name, description, db_type, host, port, database_name, username,
        password_encrypted, ssl_enabled, connection_options, created_by, updated_by
      )
      VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
      RETURNING *
    `;

    const result = await db.query(query, [
      request.name,
      request.description || null,
      request.dbType,
      request.host,
      request.port,
      request.databaseName,
      request.username,
      encryptedPassword,
      request.sslEnabled || false,
      request.connectionOptions
        ? JSON.stringify(request.connectionOptions)
        : null,
      userId,
      userId,
    ]);

    return this.mapToFlowExternalDbConnection(result[0]);
  }

  /**
   * ID로 외부 DB 연결 조회
   */
  async findById(id: number): Promise<FlowExternalDbConnection | null> {
    const query = "SELECT * FROM flow_external_db_connection WHERE id = $1";
    const result = await db.query(query, [id]);

    if (result.length === 0) {
      return null;
    }

    return this.mapToFlowExternalDbConnection(result[0]);
  }

  /**
   * 모든 외부 DB 연결 조회
   */
  async findAll(
    activeOnly: boolean = false
  ): Promise<FlowExternalDbConnection[]> {
    let query = "SELECT * FROM flow_external_db_connection";
    if (activeOnly) {
      query += " WHERE is_active = true";
    }
    query += " ORDER BY name ASC";

    const result = await db.query(query);
    return result.map((row) => this.mapToFlowExternalDbConnection(row));
  }

  /**
   * 외부 DB 연결 수정
   */
  async update(
    id: number,
    request: UpdateFlowExternalDbConnectionRequest,
    userId: string = "system"
  ): Promise<FlowExternalDbConnection | null> {
    const fields: string[] = [];
    const params: any[] = [];
    let paramIndex = 1;

    if (request.name !== undefined) {
      fields.push(`name = $${paramIndex}`);
      params.push(request.name);
      paramIndex++;
    }

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

    if (request.host !== undefined) {
      fields.push(`host = $${paramIndex}`);
      params.push(request.host);
      paramIndex++;
    }

    if (request.port !== undefined) {
      fields.push(`port = $${paramIndex}`);
      params.push(request.port);
      paramIndex++;
    }

    if (request.databaseName !== undefined) {
      fields.push(`database_name = $${paramIndex}`);
      params.push(request.databaseName);
      paramIndex++;
    }

    if (request.username !== undefined) {
      fields.push(`username = $${paramIndex}`);
      params.push(request.username);
      paramIndex++;
    }

    if (request.password !== undefined) {
      const encryptedPassword = this.encryption.encrypt(request.password);
      fields.push(`password_encrypted = $${paramIndex}`);
      params.push(encryptedPassword);
      paramIndex++;
    }

    if (request.sslEnabled !== undefined) {
      fields.push(`ssl_enabled = $${paramIndex}`);
      params.push(request.sslEnabled);
      paramIndex++;
    }

    if (request.connectionOptions !== undefined) {
      fields.push(`connection_options = $${paramIndex}`);
      params.push(
        request.connectionOptions
          ? JSON.stringify(request.connectionOptions)
          : null
      );
      paramIndex++;
    }

    if (request.isActive !== undefined) {
      fields.push(`is_active = $${paramIndex}`);
      params.push(request.isActive);
      paramIndex++;
    }

    if (fields.length === 0) {
      return this.findById(id);
    }

    fields.push(`updated_by = $${paramIndex}`);
    params.push(userId);
    paramIndex++;

    fields.push(`updated_at = NOW()`);

    const query = `
      UPDATE flow_external_db_connection
      SET ${fields.join(", ")}
      WHERE id = $${paramIndex}
      RETURNING *
    `;
    params.push(id);

    const result = await db.query(query, params);

    if (result.length === 0) {
      return null;
    }

    // 연결 풀 갱신 (비밀번호 변경 시)
    if (request.password !== undefined || request.host !== undefined) {
      this.closeConnection(id);
    }

    return this.mapToFlowExternalDbConnection(result[0]);
  }

  /**
   * 외부 DB 연결 삭제
   */
  async delete(id: number): Promise<boolean> {
    // 연결 풀 정리
    this.closeConnection(id);

    const query =
      "DELETE FROM flow_external_db_connection WHERE id = $1 RETURNING id";
    const result = await db.query(query, [id]);
    return result.length > 0;
  }

  /**
   * 연결 테스트
   */
  async testConnection(
    id: number
  ): Promise<{ success: boolean; message: string }> {
    try {
      const connection = await this.findById(id);
      if (!connection) {
        return { success: false, message: "연결 정보를 찾을 수 없습니다." };
      }

      const pool = await this.getConnectionPool(connection);

      // 간단한 쿼리로 연결 테스트
      const client = await pool.connect();
      try {
        await client.query("SELECT 1");
        return { success: true, message: "연결 성공" };
      } finally {
        client.release();
      }
    } catch (error: any) {
      return { success: false, message: error.message };
    }
  }

  /**
   * 외부 DB의 테이블 목록 조회
   */
  async getTables(
    id: number
  ): Promise<{ success: boolean; data?: string[]; message?: string }> {
    try {
      const connection = await this.findById(id);
      if (!connection) {
        return { success: false, message: "연결 정보를 찾을 수 없습니다." };
      }

      const pool = await this.getConnectionPool(connection);
      const client = await pool.connect();

      try {
        let query: string;
        switch (connection.dbType) {
          case "postgresql":
            query =
              "SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename";
            break;
          case "mysql":
            query = `SELECT table_name as tablename FROM information_schema.tables WHERE table_schema = '${connection.databaseName}' ORDER BY table_name`;
            break;
          default:
            return {
              success: false,
              message: `지원하지 않는 DB 타입: ${connection.dbType}`,
            };
        }

        const result = await client.query(query);
        const tables = result.rows.map((row: any) => row.tablename);

        return { success: true, data: tables };
      } finally {
        client.release();
      }
    } catch (error: any) {
      return { success: false, message: error.message };
    }
  }

  /**
   * 외부 DB의 특정 테이블 컬럼 목록 조회
   */
  async getTableColumns(
    id: number,
    tableName: string
  ): Promise<{
    success: boolean;
    data?: { column_name: string; data_type: string }[];
    message?: string;
  }> {
    try {
      const connection = await this.findById(id);
      if (!connection) {
        return { success: false, message: "연결 정보를 찾을 수 없습니다." };
      }

      const pool = await this.getConnectionPool(connection);
      const client = await pool.connect();

      try {
        let query: string;
        switch (connection.dbType) {
          case "postgresql":
            query = `SELECT column_name, data_type 
                     FROM information_schema.columns 
                     WHERE table_schema = 'public' AND table_name = $1 
                     ORDER BY ordinal_position`;
            break;
          case "mysql":
            query = `SELECT column_name, data_type 
                     FROM information_schema.columns 
                     WHERE table_schema = '${connection.databaseName}' AND table_name = ? 
                     ORDER BY ordinal_position`;
            break;
          default:
            return {
              success: false,
              message: `지원하지 않는 DB 타입: ${connection.dbType}`,
            };
        }

        const result = await client.query(query, [tableName]);

        return { success: true, data: result.rows };
      } finally {
        client.release();
      }
    } catch (error: any) {
      return { success: false, message: error.message };
    }
  }

  /**
   * 연결 풀 가져오기 (캐싱)
   */
  async getConnectionPool(connection: FlowExternalDbConnection): Promise<Pool> {
    if (this.connectionPools.has(connection.id)) {
      return this.connectionPools.get(connection.id)!;
    }

    // 비밀번호 복호화
    const decryptedPassword = this.encryption.decrypt(
      connection.passwordEncrypted
    );

    let pool: Pool;
    switch (connection.dbType) {
      case "postgresql":
        pool = new Pool({
          host: connection.host,
          port: connection.port,
          database: connection.databaseName,
          user: connection.username,
          password: decryptedPassword,
          ssl: connection.sslEnabled,
          // 연결 풀 설정 (고갈 방지)
          max: 10, // 최대 연결 수
          min: 2, // 최소 연결 수
          idleTimeoutMillis: 30000, // 30초 유휴 시간 후 연결 해제
          connectionTimeoutMillis: 10000, // 10초 연결 타임아웃
          ...(connection.connectionOptions || {}),
        });

        // 에러 핸들러 등록
        pool.on("error", (err) => {
          console.error(`외부 DB 연결 풀 오류 (ID: ${connection.id}):`, err);
        });
        break;
      // case "mysql":
      //   pool = mysql.createPool({ ... });
      //   break;
      // case "mssql":
      //   pool = new ConnectionPool({ ... });
      //   break;
      default:
        throw new Error(`지원하지 않는 DB 타입: ${connection.dbType}`);
    }

    this.connectionPools.set(connection.id, pool);
    return pool;
  }

  /**
   * 연결 풀 정리
   */
  closeConnection(id: number): void {
    const pool = this.connectionPools.get(id);
    if (pool) {
      pool.end();
      this.connectionPools.delete(id);
    }
  }

  /**
   * 모든 연결 풀 정리
   */
  closeAllConnections(): void {
    for (const [id, pool] of this.connectionPools.entries()) {
      pool.end();
    }
    this.connectionPools.clear();
  }

  /**
   * DB row를 FlowExternalDbConnection으로 매핑
   */
  private mapToFlowExternalDbConnection(row: any): FlowExternalDbConnection {
    return {
      id: row.id,
      name: row.name,
      description: row.description || undefined,
      dbType: row.db_type,
      host: row.host,
      port: row.port,
      databaseName: row.database_name,
      username: row.username,
      passwordEncrypted: row.password_encrypted,
      sslEnabled: row.ssl_enabled,
      connectionOptions: row.connection_options || undefined,
      isActive: row.is_active,
      createdBy: row.created_by || undefined,
      updatedBy: row.updated_by || undefined,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }
}
