/**
 * Zulip账号关联数据访问层
 * 
 * 功能描述：
 * - 提供Zulip账号关联数据的CRUD操作
 * - 封装复杂查询逻辑和数据库交互
 * - 实现数据访问层的业务逻辑抽象
 * - 支持事务操作确保数据一致性
 * - 优化查询性能和批量操作效率
 * - 集成AppLoggerService提供结构化日志
 * 
 * 职责分离：
 * - 数据访问：负责所有数据库操作和查询
 * - 事务管理：处理需要原子性的复合操作
 * - 查询优化：提供高效的数据库查询方法
 * - 性能监控：记录查询耗时和性能指标
 * - 并发控制：使用悲观锁防止竞态条件
 * 
 * 最近修改：
 * - 2026-01-15: 代码规范优化 - 清理未使用的导入FindOptionsWhere (修改者: moyin)
 * - 2026-01-12: 性能优化 - 集成AppLoggerService，优化查询和批量操作
 * - 2026-01-07: 代码规范优化 - 使用统一的常量文件，提高代码质量
 * - 2026-01-07: 代码规范优化 - 完善文件头注释和方法三级注释
 * - 2026-01-07: 功能新增 - 添加事务支持防止并发竞态条件
 * 
 * @author angjustinl
 * @version 1.2.1
 * @since 2025-01-05
 * @lastModified 2026-01-15
 */

import { Injectable, Inject } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource, SelectQueryBuilder } from 'typeorm';
import { ZulipAccounts } from './zulip_accounts.entity';
import { AppLoggerService } from '../../utils/logger/logger.service';
import {
  DEFAULT_VERIFICATION_INTERVAL,
  DEFAULT_MAX_RETRY_COUNT,
  VERIFICATION_QUERY_LIMIT,
  ERROR_ACCOUNTS_QUERY_LIMIT,
} from './zulip_accounts.constants';
import {
  CreateZulipAccountData,
  UpdateZulipAccountData,
  ZulipAccountQueryOptions,
  StatusStatistics,
  IZulipAccountsRepository,
} from './zulip_accounts.types';

// 保持向后兼容的类型别名
export type CreateZulipAccountDto = CreateZulipAccountData;
export type UpdateZulipAccountDto = UpdateZulipAccountData;
export { ZulipAccountQueryOptions };

@Injectable()
export class ZulipAccountsRepository implements IZulipAccountsRepository {
  private readonly logger: AppLoggerService;

  constructor(
    @InjectRepository(ZulipAccounts)
    private readonly repository: Repository<ZulipAccounts>,
    private readonly dataSource: DataSource,
    @Inject(AppLoggerService) logger: AppLoggerService,
  ) {
    this.logger = logger;
    this.logger.info('ZulipAccountsRepository初始化完成', {
      module: 'ZulipAccountsRepository',
      operation: 'constructor'
    });
  }

  /**
   * 创建新的Zulip账号关联（带事务支持和性能监控）
   * 
   * 业务逻辑：
   * 1. 开启数据库事务确保原子性
   * 2. 使用悲观锁检查游戏用户ID是否已存在关联
   * 3. 检查Zulip用户ID是否已被使用
   * 4. 检查Zulip邮箱是否已被使用
   * 5. 创建新的关联记录并保存
   * 6. 记录操作日志和性能指标
   * 7. 提交事务或回滚
   * 
   * @param createDto 创建数据
   * @returns Promise<ZulipAccounts> 创建的关联记录
   * @throws Error 当唯一性约束冲突时
   * 
   * @example
   * ```typescript
   * const account = await repository.create({
   *   gameUserId: BigInt(12345),
   *   zulipUserId: 67890,
   *   zulipEmail: 'user@example.com',
   *   zulipFullName: '用户名',
   *   zulipApiKeyEncrypted: 'encrypted_key'
   * });
   * ```
   */
  async create(createDto: CreateZulipAccountDto): Promise<ZulipAccounts> {
    const startTime = Date.now();
    
    this.logger.info('开始创建Zulip账号关联', {
      module: 'ZulipAccountsRepository',
      operation: 'create',
      gameUserId: createDto.gameUserId.toString(),
      zulipUserId: createDto.zulipUserId,
      zulipEmail: createDto.zulipEmail
    });

    return await this.dataSource.transaction(async manager => {
      try {
        // 使用悲观锁在事务中检查唯一性约束
        const existingByGameUser = await manager
          .createQueryBuilder(ZulipAccounts, 'za')
          .where('za.gameUserId = :gameUserId', { gameUserId: createDto.gameUserId })
          .setLock('pessimistic_write')
          .getOne();
          
        if (existingByGameUser) {
          throw new Error(`Game user ${createDto.gameUserId} already has a Zulip account`);
        }

        const existingByZulipUser = await manager
          .createQueryBuilder(ZulipAccounts, 'za')
          .where('za.zulipUserId = :zulipUserId', { zulipUserId: createDto.zulipUserId })
          .setLock('pessimistic_write')
          .getOne();
          
        if (existingByZulipUser) {
          throw new Error(`Zulip user ${createDto.zulipUserId} is already linked`);
        }

        const existingByEmail = await manager
          .createQueryBuilder(ZulipAccounts, 'za')
          .where('za.zulipEmail = :zulipEmail', { zulipEmail: createDto.zulipEmail })
          .setLock('pessimistic_write')
          .getOne();
          
        if (existingByEmail) {
          throw new Error(`Zulip email ${createDto.zulipEmail} is already linked`);
        }

        // 创建实体
        const zulipAccount = manager.create(ZulipAccounts, createDto);
        const result = await manager.save(zulipAccount);

        const duration = Date.now() - startTime;
        this.logger.info('创建Zulip账号关联成功', {
          module: 'ZulipAccountsRepository',
          operation: 'create',
          gameUserId: createDto.gameUserId.toString(),
          accountId: result.id.toString(),
          duration
        });

        return result;
      } catch (error) {
        const duration = Date.now() - startTime;
        this.logger.error('创建Zulip账号关联失败', {
          module: 'ZulipAccountsRepository',
          operation: 'create',
          gameUserId: createDto.gameUserId.toString(),
          error: error instanceof Error ? error.message : String(error),
          duration
        }, error instanceof Error ? error.stack : undefined);
        
        throw error;
      }
    });
  }

  /**
   * 根据游戏用户ID查找Zulip账号关联
   * 
   * 业务逻辑：
   * 1. 根据includeGameUser参数决定是否加载关联的游戏用户信息
   * 2. 构建查询条件，使用gameUserId作为查询键
   * 3. 执行数据库查询，返回匹配的记录或null
   * 4. 如果需要关联信息，通过relations参数加载
   * 
   * @param gameUserId 游戏用户ID，BigInt类型
   * @param includeGameUser 是否包含游戏用户信息，默认false
   * @returns Promise<ZulipAccounts | null> 关联记录或null
   * 
   * @example
   * ```typescript
   * const account = await repository.findByGameUserId(BigInt(12345), true);
   * if (account) {
   *   console.log('用户邮箱:', account.zulipEmail);
   *   console.log('游戏用户:', account.gameUser?.username);
   * }
   * ```
   */
  async findByGameUserId(gameUserId: bigint, includeGameUser: boolean = false): Promise<ZulipAccounts | null> {
    const relations = includeGameUser ? ['gameUser'] : [];
    
    return await this.repository.findOne({
      where: { gameUserId },
      relations,
    });
  }

  /**
   * 根据Zulip用户ID查找账号关联
   * 
   * 业务逻辑：
   * 1. 根据includeGameUser参数决定是否加载关联的游戏用户信息
   * 2. 构建查询条件，使用zulipUserId作为查询键
   * 3. 执行数据库查询，返回匹配的记录或null
   * 4. 如果需要关联信息，通过relations参数加载
   * 
   * @param zulipUserId Zulip用户ID，数字类型
   * @param includeGameUser 是否包含游戏用户信息，默认false
   * @returns Promise<ZulipAccounts | null> 关联记录或null
   * 
   * @example
   * ```typescript
   * const account = await repository.findByZulipUserId(67890, false);
   * if (account) {
   *   console.log('关联的游戏用户ID:', account.gameUserId.toString());
   * }
   * ```
   */
  async findByZulipUserId(zulipUserId: number, includeGameUser: boolean = false): Promise<ZulipAccounts | null> {
    const relations = includeGameUser ? ['gameUser'] : [];
    
    return await this.repository.findOne({
      where: { zulipUserId },
      relations,
    });
  }

  /**
   * 根据Zulip邮箱查找账号关联
   * 
   * @param zulipEmail Zulip邮箱
   * @param includeGameUser 是否包含游戏用户信息
   * @returns Promise<ZulipAccounts | null> 关联记录或null
   */
  async findByZulipEmail(zulipEmail: string, includeGameUser: boolean = false): Promise<ZulipAccounts | null> {
    const relations = includeGameUser ? ['gameUser'] : [];
    
    return await this.repository.findOne({
      where: { zulipEmail },
      relations,
    });
  }

  /**
   * 根据ID查找Zulip账号关联
   * 
   * @param id 关联记录ID
   * @param includeGameUser 是否包含游戏用户信息
   * @returns Promise<ZulipAccounts | null> 关联记录或null
   */
  async findById(id: bigint, includeGameUser: boolean = false): Promise<ZulipAccounts | null> {
    const relations = includeGameUser ? ['gameUser'] : [];
    
    return await this.repository.findOne({
      where: { id },
      relations,
    });
  }

  /**
   * 更新Zulip账号关联
   * 
   * @param id 关联记录ID
   * @param updateDto 更新数据
   * @returns Promise<ZulipAccounts | null> 更新后的记录或null
   */
  async update(id: bigint, updateDto: UpdateZulipAccountDto): Promise<ZulipAccounts | null> {
    const result = await this.repository.update({ id }, updateDto);
    if (result.affected === 0) {
      return null;
    }
    return await this.findById(id);
  }

  /**
   * 根据游戏用户ID更新Zulip账号关联
   * 
   * @param gameUserId 游戏用户ID
   * @param updateDto 更新数据
   * @returns Promise<ZulipAccounts | null> 更新后的记录或null
   */
  async updateByGameUserId(gameUserId: bigint, updateDto: UpdateZulipAccountDto): Promise<ZulipAccounts | null> {
    const result = await this.repository.update({ gameUserId }, updateDto);
    if (result.affected === 0) {
      return null;
    }
    return await this.findByGameUserId(gameUserId);
  }

  /**
   * 删除Zulip账号关联
   * 
   * @param id 关联记录ID
   * @returns Promise<boolean> 是否删除成功
   */
  async delete(id: bigint): Promise<boolean> {
    const result = await this.repository.delete({ id });
    return result.affected > 0;
  }

  /**
   * 根据游戏用户ID删除Zulip账号关联
   * 
   * @param gameUserId 游戏用户ID
   * @returns Promise<boolean> 是否删除成功
   */
  async deleteByGameUserId(gameUserId: bigint): Promise<boolean> {
    const result = await this.repository.delete({ gameUserId });
    return result.affected > 0;
  }

  /**
   * 查询多个Zulip账号关联（优化版本）
   * 
   * 业务逻辑：
   * 1. 构建基础查询构建器
   * 2. 根据查询选项动态添加WHERE条件
   * 3. 支持关联查询和分页
   * 4. 使用索引优化查询性能
   * 5. 记录查询日志和性能指标
   * 
   * @param options 查询选项
   * @returns Promise<ZulipAccounts[]> 关联记录列表
   */
  async findMany(options: ZulipAccountQueryOptions = {}): Promise<ZulipAccounts[]> {
    const startTime = Date.now();
    
    this.logger.debug('开始查询多个Zulip账号关联', {
      module: 'ZulipAccountsRepository',
      operation: 'findMany',
      options
    });

    try {
      const queryBuilder = this.createBaseQueryBuilder('za');
      
      // 动态添加WHERE条件
      this.applyQueryConditions(queryBuilder, options);
      
      // 处理关联查询
      if (options.includeGameUser) {
        queryBuilder.leftJoinAndSelect('za.gameUser', 'user');
      }
      
      // 添加排序和分页
      queryBuilder
        .orderBy('za.createdAt', 'DESC')
        .addOrderBy('za.id', 'DESC'); // 添加第二排序字段确保结果稳定
      
      // 如果有分页需求，可以在这里添加
      // if (options.limit) queryBuilder.limit(options.limit);
      // if (options.offset) queryBuilder.offset(options.offset);

      const results = await queryBuilder.getMany();
      
      const duration = Date.now() - startTime;
      this.logger.debug('查询多个Zulip账号关联完成', {
        module: 'ZulipAccountsRepository',
        operation: 'findMany',
        resultCount: results.length,
        duration
      });

      return results;
    } catch (error) {
      const duration = Date.now() - startTime;
      this.logger.error('查询多个Zulip账号关联失败', {
        module: 'ZulipAccountsRepository',
        operation: 'findMany',
        options,
        error: error instanceof Error ? error.message : String(error),
        duration
      }, error instanceof Error ? error.stack : undefined);
      
      throw error;
    }
  }

  /**
   * 获取需要验证的账号列表（优化查询）
   * 
   * 业务逻辑：
   * 1. 计算验证截止时间（当前时间减去最大验证间隔）
   * 2. 查询状态为active的账号
   * 3. 筛选从未验证或验证时间超期的账号
   * 4. 按验证时间升序排序，NULL值优先
   * 5. 限制查询数量避免性能问题
   * 
   * @param maxAge 最大验证间隔（毫秒），默认24小时
   * @returns Promise<ZulipAccounts[]> 需要验证的账号列表
   * 
   * @example
   * ```typescript
   * const accounts = await repository.findAccountsNeedingVerification();
   * console.log(`需要验证的账号数量: ${accounts.length}`);
   * ```
   */
  async findAccountsNeedingVerification(maxAge: number = DEFAULT_VERIFICATION_INTERVAL): Promise<ZulipAccounts[]> {
    const cutoffTime = new Date(Date.now() - maxAge);
    
    return await this.repository
      .createQueryBuilder('za')
      .where('za.status = :status', { status: 'active' })
      .andWhere(
        '(za.last_verified_at IS NULL OR za.last_verified_at < :cutoffTime)',
        { cutoffTime }
      )
      .orderBy('za.last_verified_at', 'ASC', 'NULLS FIRST')
      .limit(VERIFICATION_QUERY_LIMIT) // 限制查询数量，避免性能问题
      .getMany();
  }

  /**
   * 获取错误状态的账号列表（可重试的）
   * 
   * 业务逻辑：
   * 1. 查询状态为error的账号
   * 2. 筛选重试次数小于最大重试次数的账号
   * 3. 按更新时间升序排序，优先处理较早的错误
   * 4. 限制查询数量避免性能问题
   * 
   * @param maxRetryCount 最大重试次数，默认3次
   * @returns Promise<ZulipAccounts[]> 错误状态的账号列表
   * 
   * @example
   * ```typescript
   * const errorAccounts = await repository.findErrorAccounts(5);
   * console.log(`可重试的错误账号: ${errorAccounts.length}`);
   * ```
   */
  async findErrorAccounts(maxRetryCount: number = DEFAULT_MAX_RETRY_COUNT): Promise<ZulipAccounts[]> {
    return await this.repository
      .createQueryBuilder('za')
      .where('za.status = :status', { status: 'error' })
      .andWhere('za.retry_count < :maxRetryCount', { maxRetryCount })
      .orderBy('za.updated_at', 'ASC')
      .limit(ERROR_ACCOUNTS_QUERY_LIMIT) // 限制查询数量
      .getMany();
  }

  /**
   * 批量更新账号状态
   * 
   * @param ids 账号ID列表
   * @param status 新状态
   * @returns Promise<number> 更新的记录数
   */
  async batchUpdateStatus(ids: bigint[], status: 'active' | 'inactive' | 'suspended' | 'error'): Promise<number> {
    const result = await this.repository
      .createQueryBuilder()
      .update(ZulipAccounts)
      .set({ status })
      .whereInIds(ids)
      .execute();
    
    return result.affected || 0;
  }

  /**
   * 统计各状态的账号数量（优化查询）
   * 
   * @returns Promise<StatusStatistics> 状态统计
   */
  async getStatusStatistics(): Promise<StatusStatistics> {
    const result = await this.repository
      .createQueryBuilder('za')
      .select('za.status', 'status')
      .addSelect('COUNT(*)', 'count')
      .groupBy('za.status')
      .getRawMany();

    const statistics: StatusStatistics = {
      active: 0,
      inactive: 0,
      suspended: 0,
      error: 0,
    };

    result.forEach(row => {
      statistics[row.status] = parseInt(row.count, 10);
    });

    return statistics;
  }

  /**
   * 检查邮箱是否已存在
   * 
   * @param zulipEmail Zulip邮箱
   * @param excludeId 排除的记录ID（用于更新时检查）
   * @returns Promise<boolean> 是否已存在
   */
  async existsByEmail(zulipEmail: string, excludeId?: bigint): Promise<boolean> {
    const queryBuilder = this.repository
      .createQueryBuilder('za')
      .where('za.zulip_email = :zulipEmail', { zulipEmail });

    if (excludeId) {
      queryBuilder.andWhere('za.id != :excludeId', { excludeId });
    }

    const count = await queryBuilder.getCount();
    return count > 0;
  }

  /**
   * 检查Zulip用户ID是否已存在
   * 
   * @param zulipUserId Zulip用户ID
   * @param excludeId 排除的记录ID（用于更新时检查）
   * @returns Promise<boolean> 是否已存在
   */
  async existsByZulipUserId(zulipUserId: number, excludeId?: bigint): Promise<boolean> {
    const queryBuilder = this.repository
      .createQueryBuilder('za')
      .where('za.zulip_user_id = :zulipUserId', { zulipUserId });

    if (excludeId) {
      queryBuilder.andWhere('za.id != :excludeId', { excludeId });
    }

    const count = await queryBuilder.getCount();
    return count > 0;
  }

  /**
   * 检查游戏用户ID是否已存在
   * 
   * @param gameUserId 游戏用户ID
   * @param excludeId 排除的记录ID（用于更新时检查）
   * @returns Promise<boolean> 是否已存在
   */
  async existsByGameUserId(gameUserId: bigint, excludeId?: bigint): Promise<boolean> {
    const queryBuilder = this.repository
      .createQueryBuilder('za')
      .where('za.game_user_id = :gameUserId', { gameUserId });

    if (excludeId) {
      queryBuilder.andWhere('za.id != :excludeId', { excludeId });
    }

    const count = await queryBuilder.getCount();
    return count > 0;
  }

  // ========== 辅助方法 ==========

  /**
   * 创建基础查询构建器
   * 
   * @param alias 表别名
   * @returns SelectQueryBuilder<ZulipAccounts>
   * @private
   */
  private createBaseQueryBuilder(alias: string = 'za'): SelectQueryBuilder<ZulipAccounts> {
    return this.repository.createQueryBuilder(alias);
  }

  /**
   * 应用查询条件
   * 
   * @param queryBuilder 查询构建器
   * @param options 查询选项
   * @private
   */
  private applyQueryConditions(
    queryBuilder: SelectQueryBuilder<ZulipAccounts>, 
    options: ZulipAccountQueryOptions
  ): void {
    if (options.gameUserId) {
      queryBuilder.andWhere('za.gameUserId = :gameUserId', { gameUserId: options.gameUserId });
    }
    
    if (options.zulipUserId) {
      queryBuilder.andWhere('za.zulipUserId = :zulipUserId', { zulipUserId: options.zulipUserId });
    }
    
    if (options.zulipEmail) {
      queryBuilder.andWhere('za.zulipEmail = :zulipEmail', { zulipEmail: options.zulipEmail });
    }
    
    if (options.status) {
      queryBuilder.andWhere('za.status = :status', { status: options.status });
    }
  }

  /**
   * 记录查询性能指标
   * 
   * @param operation 操作名称
   * @param startTime 开始时间
   * @param resultCount 结果数量
   * @private
   */
  private logQueryPerformance(operation: string, startTime: number, resultCount?: number): void {
    const duration = Date.now() - startTime;
    
    this.logger.debug('查询性能指标', {
      module: 'ZulipAccountsRepository',
      operation,
      duration,
      resultCount,
      timestamp: new Date().toISOString()
    });

    // 如果查询时间超过阈值，记录警告
    if (duration > 1000) { // 1秒阈值
      this.logger.warn('查询耗时过长', {
        module: 'ZulipAccountsRepository',
        operation,
        duration,
        resultCount,
        threshold: 1000
      });
    }
  }
}