/**
 * @文件: db-singleton.ts
 * @作者: 阿瑞
 * @功能: 真正的全局单例数据库连接管理
 * @版本: 1.2
 */

import { createPool, Pool, PoolConnection, createConnection } from 'mysql2/promise';
import DbConfig from './config';
import { SystemDBInitializer } from './init-system-db';

// 定义一个全局接口，扩展NodeJS的全局命名空间
declare global {
  // eslint-disable-next-line no-var
  var dbManagerInstance: DatabaseManager | undefined;
  // eslint-disable-next-line no-var
  var dbInitStatus: DBInitState | undefined;
  // eslint-disable-next-line no-var
  var dbInitPromise: Promise<void> | undefined;
}

/**
 * 数据库初始化状态
 */
type DBInitState = 'not_started' | 'initializing' | 'ready' | 'failed';

/**
 * 数据库连接管理器
 * 通过Node.js全局变量实现真正的单例
 */
export class DatabaseManager {
  private pool: Pool | null = null;
  private error: Error | null = null;

  /**
   * 获取连接池
   */
  public getPool(): Pool | null {
    return this.pool;
  }

  /**
   * 获取当前状态
   */
  public getState(): DBInitState {
    return global.dbInitStatus || 'not_started';
  }

  /**
   * 获取错误信息
   */
  public getError(): Error | null {
    return this.error;
  }

  /**
   * 设置全局状态
   */
  private setState(state: DBInitState): void {
    global.dbInitStatus = state;
  }

  /**
   * 确保系统数据库存在
   * 检查数据库是否存在，不存在则创建
   */
  private async ensureSystemDBExists(): Promise<boolean> {
    try {
      // 首先创建一个不指定数据库的连接
      const rootConnection = await createConnection({
        host: DbConfig.host,
        port: DbConfig.port,
        user: DbConfig.adminUser,
        password: DbConfig.adminPassword
      });

      try {
        // 检查数据库是否存在
        const [rows] = await rootConnection.query(
          `SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?`,
          [DbConfig.database]
        );

        const dbExists = Array.isArray(rows) && (rows as Record<string, unknown>[]).length > 0;
        
        if (!dbExists) {
          console.log(`🔶 数据库 [${DbConfig.database}] 不存在，正在创建...`);
          
          // 创建数据库
          await rootConnection.query(`CREATE DATABASE IF NOT EXISTS \`${DbConfig.database}\` 
                                     CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`);
          
          console.log(`🟢 数据库 [${DbConfig.database}] 创建成功`);
          
          // 初始化系统数据库表结构
          await SystemDBInitializer.initialize();
          
          // 初始化默认数据结构（不创建用户）
          await SystemDBInitializer.initDefaultData();
          
          console.log('🟢 系统数据库结构初始化完成');
        } else {
          console.log(`🟢 数据库 [${DbConfig.database}] 已存在，跳过创建步骤`);
        }
        
        return true;
      } finally {
        // 确保关闭根连接
        await rootConnection.end();
      }
    } catch (error) {
      console.error('🔴 数据库初始化错误:', error);
      return false;
    }
  }

  /**
   * 初始化数据库连接
   * 确保数据库存在并创建连接池
   */
  public async initialize(): Promise<void> {
    // 检查全局状态
    const currentState = this.getState();
    
    // 如果已经初始化完成，直接返回
    if (currentState === 'ready' && this.pool) {
      return Promise.resolve();
    }
    
    // 如果正在初始化中，返回已有的初始化Promise
    if (currentState === 'initializing' && global.dbInitPromise) {
      return global.dbInitPromise;
    }
    
    // 设置状态为初始化中
    this.setState('initializing');
    
    // 创建初始化Promise并存入全局变量
    const initPromise = (async () => {
      try {
        // 确保数据库存在
        const dbReady = await this.ensureSystemDBExists();
        
        if (!dbReady) {
          throw new Error('无法确保系统数据库存在，连接初始化失败');
        }
        
        // 检查是否已有连接池
        if (!this.pool) {
          // 创建连接池
          const pool = createPool({
            host: DbConfig.host,
            port: DbConfig.port,
            user: DbConfig.adminUser,
            password: DbConfig.adminPassword,
            database: DbConfig.database,
            waitForConnections: true,
            connectionLimit: 10,
            queueLimit: 0
          });
          
          // 测试连接
          const connection = await pool.getConnection();
          console.log(`🟢 MySQL 已连接: ${DbConfig.host}:${DbConfig.port}/${DbConfig.database}`);
          connection.release();
          
          // 更新状态
          this.pool = pool;
          console.log('🟢 系统数据库连接池初始化完成 (首次)');
        } else {
          console.log('ℹ️ 系统数据库连接池已存在，跳过初始化');
        }
        
        // 设置状态为就绪
        this.setState('ready');
      } catch (error) {
        this.error = error as Error;
        this.setState('failed');
        console.error('🔴 数据库初始化失败:', error);
        
        // 60秒后重置状态，允许重试
        setTimeout(() => {
          this.setState('not_started');
          global.dbInitPromise = undefined;
          console.log('🔄 重置数据库初始化状态，允许重试...');
        }, 60000);
        
        throw error;
      }
    })();
    
    // 存储初始化Promise到全局变量
    global.dbInitPromise = initPromise;
    
    return initPromise;
  }

  /**
   * 等待数据库初始化完成
   * 若超时则返回错误
   */
  public async waitForReady(timeoutMs = 10000): Promise<boolean> {
    const start = Date.now();
    
    // 如果初始化尚未开始，则启动初始化
    if (this.getState() === 'not_started') {
      this.initialize().catch(err => {
        console.error('初始化过程中出错:', err);
      });
    }
    
    // 等待初始化完成或超时
    while (this.getState() === 'initializing') {
      if (Date.now() - start > timeoutMs) {
        console.log('⚠️ 等待数据库初始化超时');
        return false;
      }
      
      // 等待100毫秒再检查
      await new Promise(resolve => setTimeout(resolve, 100));
    }
    
    // 返回是否初始化成功
    return this.getState() === 'ready';
  }

  /**
   * 获取数据库连接
   */
  public async getConnection(): Promise<PoolConnection> {
    // 如果数据库未初始化，先尝试初始化
    if (!this.pool) {
      const isReady = await this.waitForReady();
      if (!isReady || !this.pool) {
        throw new Error('数据库连接池初始化失败');
      }
    }
    return this.pool.getConnection();
  }
  
  /**
   * 关闭连接池
   * 仅用于测试和特殊场景
   */
  public async close(): Promise<void> {
    if (this.pool) {
      await this.pool.end();
      this.pool = null;
      this.setState('not_started');
      global.dbInitPromise = undefined;
      console.log('🟠 数据库连接池已关闭');
    }
  }
}

// 检查全局实例，确保单例模式
let dbManager: DatabaseManager;

// 检查是否有全局实例存在
if (global.dbManagerInstance) {
  // 使用现有实例
  dbManager = global.dbManagerInstance;
  // 开发环境下打印一条低优先级信息
  if (process.env.NODE_ENV !== 'production') {
    console.debug('ℹ️ 使用现有数据库管理器实例');
  }
} else {
  // 创建新实例并存入全局
  dbManager = new DatabaseManager();
  global.dbManagerInstance = dbManager;
  
  // 应用启动时立即开始初始化数据库（仅在首次创建实例时）
  console.log('🔄 创建数据库管理器实例并开始初始化...');
  dbManager.initialize().catch(error => {
    console.error('🔴 数据库初始化异常:', error);
  });
}

export default dbManager; 