import * as mysql from "mysql2/promise";
import {
  DatabaseConnector,
  ConnectionConfig,
  QueryResult,
} from "../interfaces/DatabaseConnector";
import { ConnectionTestResult, TableInfo } from "../types/externalDbTypes";

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

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

  async connect(): Promise<void> {
    if (this.connection) {
      throw new Error("이미 연결되어 있습니다.");
    }

    this.connection = await mysql.createConnection({
      host: this.config.host,
      port: this.config.port,
      database: this.config.database,
      user: this.config.user,
      password: this.config.password,
      connectTimeout: this.config.connectionTimeoutMillis || 30000,
      ssl: this.config.ssl ? { rejectUnauthorized: false } : undefined,
    });
  }

  async disconnect(): Promise<void> {
    if (this.connection) {
      await this.connection.end();
      this.connection = null;
    }
  }

  async testConnection(): Promise<ConnectionTestResult> {
    const startTime = Date.now();
    try {
      await this.connect();

      const [versionResult] = await this.connection!.query(
        "SELECT VERSION() as version"
      );
      const [sizeResult] = await this.connection!.query(
        "SELECT SUM(data_length + index_length) as size FROM information_schema.tables WHERE table_schema = DATABASE()"
      );

      const responseTime = Date.now() - startTime;

      return {
        success: true,
        message: "MySQL 연결이 성공했습니다.",
        details: {
          response_time: responseTime,
          server_version: (versionResult as any)[0]?.version || "알 수 없음",
          database_size: this.formatBytes(
            parseInt((sizeResult as any)[0]?.size || "0")
          ),
        },
      };
    } catch (error) {
      return {
        success: false,
        message: "MySQL 연결에 실패했습니다.",
        error: {
          code: "CONNECTION_FAILED",
          details: error instanceof Error ? error.message : "알 수 없는 오류",
        },
      };
    } finally {
      await this.disconnect();
    }
  }

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

    try {
      const [rows, fields] = await this.connection!.query(query, params);
      return {
        rows: rows as any[],
        rowCount: Array.isArray(rows) ? rows.length : 0,
        fields: (fields as mysql.FieldPacket[]).map((field) => ({
          name: field.name,
          dataType: field.type?.toString() || "unknown",
        })),
      };
    } finally {
      await this.disconnect();
    }
  }

  async getTables(): Promise<TableInfo[]> {
    if (!this.connection) {
      await this.connect();
    }

    try {
      const [tables] = await this.connection!.query(`
        SELECT 
          t.TABLE_NAME as table_name,
          t.TABLE_COMMENT as table_description
        FROM information_schema.TABLES t
        WHERE t.TABLE_SCHEMA = DATABASE()
        ORDER BY t.TABLE_NAME
      `);

      const result: TableInfo[] = [];

      for (const table of tables as any[]) {
        const [columns] = await this.connection!.query(
          `
          SELECT 
            COLUMN_NAME as column_name,
            DATA_TYPE as data_type,
            IS_NULLABLE as is_nullable,
            COLUMN_DEFAULT as column_default
          FROM information_schema.COLUMNS
          WHERE TABLE_SCHEMA = DATABASE()
            AND TABLE_NAME = ?
          ORDER BY ORDINAL_POSITION
        `,
          [table.table_name]
        );

        result.push({
          table_name: table.table_name,
          description: table.table_description || null,
          columns: columns as any[],
        });
      }

      return result;
    } finally {
      await this.disconnect();
    }
  }

  async getColumns(tableName: string): Promise<
    Array<{
      name: string;
      dataType: string;
      isNullable: boolean;
      defaultValue?: string;
    }>
  > {
    if (!this.connection) {
      await this.connect();
    }

    try {
      const [columns] = await this.connection!.query(
        `
        SELECT 
          COLUMN_NAME as name,
          DATA_TYPE as dataType,
          IS_NULLABLE = 'YES' as isNullable,
          COLUMN_DEFAULT as defaultValue
        FROM information_schema.COLUMNS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = ?
        ORDER BY ORDINAL_POSITION
      `,
        [tableName]
      );

      return (columns as any[]).map((col) => ({
        name: col.name,
        dataType: col.dataType,
        isNullable: col.isNullable,
        defaultValue: col.defaultValue,
      }));
    } finally {
      await this.disconnect();
    }
  }

  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];
  }
}
