/**
 * @文件: teamdb-singleton.ts
 * @作者: 阿瑞
 * @功能: 团队数据库连接池管理单例
 * @版本: 1.2
 */

import { createPool, Pool } from 'mysql2/promise';
import { ITeam } from '@/models/system/types';
import DbConfig from './config';

/**
 * 定义一个全局接口，扩展NodeJS的全局命名空间
 */
declare global {
  // eslint-disable-next-line no-var
  var teamDbManagerInstance: TeamDatabaseManager | undefined;
}

/**
 * 团队数据库连接信息
 */
export interface TeamDbInfo {
  dbHost: string;
  dbName: string;
  dbUser: string;
  dbPassword: string;
}

/**
 * 团队数据库连接池管理器
 * 通过Node.js全局变量实现真正的单例
 */
export class TeamDatabaseManager {
  /**
   * 团队数据库连接池缓存
   * 键为团队数据库连接标识，值为对应的连接池
   */
  private teamDbPools: Map<string, Pool> = new Map();

  /**
   * 团队信息缓存
   * 键为团队代码，值为对应的连接信息
   */
  private teamInfoCache: Map<string, TeamDbInfo> = new Map();

  /**
   * 系统数据库连接池（仅用于查询团队信息）
   */
  private systemPool: Pool | null = null;
  
  /**
   * 系统数据库是否初始化中的标志
   */
  private systemPoolInitializing = false;
  
  /**
   * 系统数据库初始化Promise
   */
  private systemPoolInitPromise: Promise<Pool> | null = null;

  /**
   * 初始化系统数据库连接池
   * 仅在需要查询团队信息时创建
   */
  private async initSystemPool(): Promise<Pool> {
    // 如果已经有初始化好的连接池，直接返回
    if (this.systemPool) {
      return this.systemPool;
    }
    
    // 如果已经有初始化中的Promise，等待其完成
    if (this.systemPoolInitializing && this.systemPoolInitPromise) {
      return this.systemPoolInitPromise;
    }
    
    this.systemPoolInitializing = true;
    
    // 创建初始化Promise
    this.systemPoolInitPromise = (async () => {
      // 避免重复日志
      console.log('初始化团队管理器的系统数据库连接池');
      
      const pool = createPool({
        host: DbConfig.host,
        port: DbConfig.port,
        user: DbConfig.adminUser,
        password: DbConfig.adminPassword,
        database: DbConfig.database,
        waitForConnections: true,
        connectionLimit: 2, // 仅用于查询团队信息，连接数少一些
        queueLimit: 0
      });
      
      // 测试连接
      try {
        const conn = await pool.getConnection();
        console.log(`团队管理器系统数据库连接池测试成功`);
        conn.release();
        this.systemPool = pool;
        return pool;
      } catch (error) {
        console.error('团队管理器系统数据库连接池初始化失败:', error);
        this.systemPoolInitializing = false;
        this.systemPoolInitPromise = null;
        throw error;
      }
    })();
    
    try {
      const result = await this.systemPoolInitPromise;
      this.systemPoolInitializing = false;
      return result;
    } catch (error) {
      this.systemPoolInitializing = false;
      this.systemPoolInitPromise = null;
      throw error;
    }
  }

  /**
   * 获取团队数据库连接池标识
   */
  private getPoolKey(dbHost: string, dbName: string, dbUser: string): string {
    return `${dbHost}:${dbName}:${dbUser}`;
  }

  /**
   * 获取团队数据库连接池
   * 如果不存在则创建新池并返回
   * 
   * @param teamDbInfo 团队数据库连接信息
   * @returns 数据库连接池
   */
  public getTeamDbPool(teamDbInfo: TeamDbInfo): Pool {
    const { dbHost, dbName, dbUser, dbPassword } = teamDbInfo;

    // 创建连接池唯一标识
    const poolKey = this.getPoolKey(dbHost, dbName, dbUser);
    
    // 检查缓存中是否存在该连接池
    if (!this.teamDbPools.has(poolKey)) {
      console.log(`创建团队数据库连接池: ${dbHost}/${dbName}`);
      
      // 创建新的连接池
      const newPool = createPool({
        host: dbHost,
        database: dbName,
        user: dbUser,
        password: dbPassword,
        waitForConnections: true,
        connectionLimit: 5, // 每个团队连接池连接数少一些
        queueLimit: 0
      });
      
      // 测试连接并记录日志（异步操作，不阻塞）
      newPool.getConnection()
        .then(conn => {
          console.log(`🟢 团队数据库连接测试成功: ${dbHost}/${dbName}`);
          conn.release();
        })
        .catch(err => {
          console.error(`🔴 团队数据库连接测试失败: ${dbHost}/${dbName}`, err);
        });
      
      // 存入缓存
      this.teamDbPools.set(poolKey, newPool);
    }
    
    return this.teamDbPools.get(poolKey)!;
  }

  /**
   * 根据团队代码获取数据库连接信息
   * 独立查询系统数据库，不依赖db-singleton
   * 
   * @param teamCode 团队代码
   * @returns 团队数据库连接信息
   */
  public async getTeamDBInfoByCode(teamCode: string): Promise<TeamDbInfo | null> {
    // 首先检查缓存
    if (this.teamInfoCache.has(teamCode)) {
      console.log(`从缓存获取团队[${teamCode}]数据库连接信息`);
      return this.teamInfoCache.get(teamCode)!;
    }

    try {
      console.log(`开始查询团队[${teamCode}]数据库连接信息`);

      // 初始化系统数据库连接池并获取连接
      const systemPool = await this.initSystemPool();
      const connection = await systemPool.getConnection();

      try {
        // 查询团队信息
        const [rows] = await connection.query(
          `SELECT 
            id, team_code, db_host, db_name, db_username, db_password 
           FROM teams 
           WHERE team_code = ? AND status = 1`,
          [teamCode]
        );

        // 释放连接
        connection.release();

        // 处理查询结果
        const teams = Array.isArray(rows) ? rows : [];

        if (teams.length === 0) {
          console.error(`未找到团队[${teamCode}]或团队已被禁用`);
          return null;
        }

        const team = teams[0] as ITeam;

        // 检查必要的连接信息是否齐全
        if (!team.db_host || !team.db_name || !team.db_username || !team.db_password) {
          console.error(`团队[${teamCode}]数据库连接信息不完整`);
          return null;
        }

        // 创建连接信息对象
        const teamDbInfo: TeamDbInfo = {
          dbHost: team.db_host,
          dbName: team.db_name,
          dbUser: team.db_username,
          dbPassword: team.db_password
        };

        console.log(`获取到团队[${teamCode}]数据库连接信息: ${teamDbInfo.dbHost}/${teamDbInfo.dbName}`);

        // 存入缓存
        this.teamInfoCache.set(teamCode, teamDbInfo);

        return teamDbInfo;

      } catch (error) {
        connection.release(); // 确保在发生错误时也释放连接
        throw error;
      }
    } catch (error) {
      console.error('获取团队数据库连接信息失败:', error);
      return null;
    }
  }

  /**
   * 根据团队对象获取数据库连接信息
   * 这个方法对于已经有团队对象的场景有用
   * 
   * @param team 团队信息对象
   * @returns 团队数据库连接信息
   */
  public getTeamDbInfoFromTeam(team: ITeam): TeamDbInfo | null {
    // 检查必要的连接信息是否齐全
    if (!team.db_host || !team.db_name || !team.db_username || !team.db_password) {
      console.error(`团队[${team.team_code}]数据库连接信息不完整`);
      return null;
    }
    
    const teamDbInfo: TeamDbInfo = {
      dbHost: team.db_host,
      dbName: team.db_name,
      dbUser: team.db_username,
      dbPassword: team.db_password
    };

    // 更新缓存
    if (team.team_code) {
      this.teamInfoCache.set(team.team_code, teamDbInfo);
    }
    
    return teamDbInfo;
  }

  /**
   * 关闭所有连接池并清除缓存
   * 通常用于应用关闭时
   */
  public async closeAllPools(): Promise<void> {
    const promises: Promise<void>[] = [];
    
    // 关闭所有团队数据库连接池
    for (const [key, pool] of this.teamDbPools.entries()) {
      promises.push(
        pool.end().then(() => {
          console.log(`🟢 关闭团队数据库连接池: ${key}`);
        }).catch(err => {
          console.error(`🔴 关闭团队数据库连接池失败: ${key}`, err);
        })
      );
    }

    // 关闭系统数据库连接池
    if (this.systemPool) {
      promises.push(
        this.systemPool.end().then(() => {
          console.log('🟢 关闭团队管理器的系统数据库连接池');
        }).catch(err => {
          console.error('🔴 关闭团队管理器的系统数据库连接池失败:', err);
        })
      );
    }
    
    await Promise.all(promises);
    
    // 清空缓存
    this.teamDbPools.clear();
    this.teamInfoCache.clear();
    this.systemPool = null;
    this.systemPoolInitPromise = null;
    this.systemPoolInitializing = false;
    
    console.log('🟢 所有团队数据库资源已清理');
  }

  /**
   * 清除特定团队的缓存
   * 当团队信息更新时使用
   * 
   * @param teamCode 团队代码
   */
  public clearTeamCache(teamCode: string): void {
    this.teamInfoCache.delete(teamCode);
  }
}

// 利用Node.js全局对象存储单例，确保在热重载和跨API路由中仍然是单例
const teamDbManager = global.teamDbManagerInstance || new TeamDatabaseManager();

// 仅在开发环境下保存到全局变量，以支持热重载
if (process.env.NODE_ENV !== 'production') {
  global.teamDbManagerInstance = teamDbManager;
}

export default teamDbManager; 