/**
 * Zulip账号关联实体
 * 
 * 功能描述：
 * - 存储游戏用户与Zulip账号的关联关系
 * - 管理Zulip账号的基本信息和状态
 * - 提供账号验证和同步功能
 * - 支持多种状态管理和业务判断方法
 * 
 * 职责分离：
 * - 数据模型定义：定义数据库表结构和字段约束
 * - 业务方法：提供账号状态判断和操作方法
 * - 关联关系：管理与Users表的一对一关系
 * 
 * 最近修改：
 * - 2026-01-07: 代码规范优化 - 使用统一的常量文件，提高代码质量
 * - 2026-01-07: 代码规范优化 - 完善文件头注释和方法注释规范
 * - 2026-01-07: 功能新增 - 添加数据库唯一约束和复合索引
 * - 2026-01-07: 功能新增 - 新增多个业务判断方法(isHealthy, canBeDeleted等)
 * 
 * @author angjustinl
 * @version 1.1.1
 * @since 2025-01-05
 * @lastModified 2026-01-07
 */

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToOne, JoinColumn, Index } from 'typeorm';
import { Users } from '../users/users.entity';
import {
  DEFAULT_MAX_AGE_DAYS,
  DEFAULT_VERIFICATION_HOURS,
  DEFAULT_MAX_RETRY_COUNT,
  HIGH_RETRY_THRESHOLD,
  MILLISECONDS_PER_HOUR,
  MILLISECONDS_PER_DAY,
} from './zulip_accounts.constants';

@Entity('zulip_accounts')
@Index(['gameUserId']) // 普通索引，不是唯一索引
@Index(['zulipUserId'], { unique: true })
@Index(['zulipEmail'], { unique: true })
@Index(['status']) // 单独的status索引
@Index(['createdAt']) // 单独的created_at索引
@Index(['status', 'lastVerifiedAt']) // 复合索引用于查询优化
@Index(['status', 'updatedAt']) // 复合索引用于查询优化
export class ZulipAccounts {
  /**
   * 主键ID
   */
  @PrimaryGeneratedColumn('increment', { type: 'bigint' })
  id: bigint;

  /**
   * 关联的游戏用户ID
   */
  @Column({ type: 'bigint', name: 'game_user_id', comment: '关联的游戏用户ID' })
  gameUserId: bigint;

  /**
   * Zulip用户ID
   */
  @Column({ type: 'int', name: 'zulip_user_id', comment: 'Zulip服务器上的用户ID' })
  zulipUserId: number;

  /**
   * Zulip用户邮箱
   */
  @Column({ type: 'varchar', length: 255, name: 'zulip_email', comment: 'Zulip账号邮箱地址' })
  zulipEmail: string;

  /**
   * Zulip用户全名
   */
  @Column({ type: 'varchar', length: 100, name: 'zulip_full_name', comment: 'Zulip账号全名' })
  zulipFullName: string;

  /**
   * Zulip API Key（加密存储）
   */
  @Column({ type: 'text', name: 'zulip_api_key_encrypted', comment: '加密存储的Zulip API Key' })
  zulipApiKeyEncrypted: string;

  /**
   * 账号状态
   * - active: 正常激活状态
   * - inactive: 未激活状态
   * - suspended: 暂停状态
   * - error: 错误状态
   */
  @Column({ 
    type: 'enum', 
    enum: ['active', 'inactive', 'suspended', 'error'], 
    default: 'active',
    comment: '账号状态：active-正常，inactive-未激活，suspended-暂停，error-错误'
  })
  status: 'active' | 'inactive' | 'suspended' | 'error';

  /**
   * 最后验证时间
   */
  @Column({ type: 'timestamp', name: 'last_verified_at', nullable: true, comment: '最后一次验证Zulip账号的时间' })
  lastVerifiedAt: Date | null;

  /**
   * 最后同步时间
   */
  @Column({ type: 'timestamp', name: 'last_synced_at', nullable: true, comment: '最后一次同步数据的时间' })
  lastSyncedAt: Date | null;

  /**
   * 错误信息
   */
  @Column({ type: 'text', name: 'error_message', nullable: true, comment: '最后一次操作的错误信息' })
  errorMessage: string | null;

  /**
   * 重试次数
   */
  @Column({ type: 'int', name: 'retry_count', default: 0, comment: '创建或同步失败的重试次数' })
  retryCount: number;

  /**
   * 创建时间
   */
  @CreateDateColumn({ name: 'created_at', comment: '记录创建时间' })
  createdAt: Date;

  /**
   * 更新时间
   */
  @UpdateDateColumn({ name: 'updated_at', comment: '记录最后更新时间' })
  updatedAt: Date;

  /**
   * 关联的游戏用户
   */
  @OneToOne(() => Users, user => user.zulipAccount)
  @JoinColumn({ name: 'game_user_id' })
  gameUser: Users;

  /**
   * 检查账号是否处于正常状态
   * 
   * 业务逻辑：
   * 1. 检查账号状态是否为'active'
   * 2. 返回布尔值表示是否正常
   * 
   * @returns boolean 是否为正常状态
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.status = 'active';
   * console.log(account.isActive()); // true
   * ```
   */
  isActive(): boolean {
    return this.status === 'active';
  }

  /**
   * 检查账号是否健康（正常且重试次数不多）
   * 
   * 业务逻辑：
   * 1. 检查账号状态是否为'active'
   * 2. 检查重试次数是否小于默认阈值
   * 3. 两个条件都满足才认为健康
   * 
   * @returns boolean 是否健康
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.status = 'active';
   * account.retryCount = 1;
   * console.log(account.isHealthy()); // true
   * ```
   */
  isHealthy(): boolean {
    return this.status === 'active' && this.retryCount < DEFAULT_MAX_RETRY_COUNT;
  }

  /**
   * 检查账号是否可以被删除
   * 
   * 业务逻辑：
   * 1. 如果账号状态不是'active'，可以删除
   * 2. 如果重试次数超过高阈值，可以删除
   * 3. 满足任一条件即可删除
   * 
   * @returns boolean 是否可以删除
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.status = 'error';
   * account.retryCount = 6;
   * console.log(account.canBeDeleted()); // true
   * ```
   */
  canBeDeleted(): boolean {
    return this.status !== 'active' || this.retryCount > HIGH_RETRY_THRESHOLD;
  }

  /**
   * 检查账号数据是否过期
   * 
   * 业务逻辑：
   * 1. 获取当前时间
   * 2. 计算与最后更新时间的差值
   * 3. 比较差值是否超过最大年龄限制
   * 
   * @param maxAge 最大年龄（毫秒），默认7天
   * @returns boolean 是否过期
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.updatedAt = new Date(Date.now() - 8 * 24 * 60 * 60 * 1000);
   * console.log(account.isStale()); // true (超过7天)
   * ```
   */
  isStale(maxAge: number = DEFAULT_MAX_AGE_DAYS * MILLISECONDS_PER_DAY): boolean {
    const now = new Date();
    const timeDiff = now.getTime() - this.updatedAt.getTime();
    return timeDiff > maxAge;
  }

  /**
   * 检查账号是否需要重新验证
   * 
   * 业务逻辑：
   * 1. 如果从未验证过，需要验证
   * 2. 计算距离上次验证的时间差
   * 3. 比较时间差是否超过最大验证间隔
   * 
   * @param maxAge 最大验证间隔（毫秒），默认24小时
   * @returns boolean 是否需要重新验证
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.lastVerifiedAt = null;
   * console.log(account.needsVerification()); // true
   * ```
   */
  needsVerification(maxAge: number = DEFAULT_VERIFICATION_HOURS * MILLISECONDS_PER_HOUR): boolean {
    if (!this.lastVerifiedAt) {
      return true;
    }
    
    const now = new Date();
    const timeDiff = now.getTime() - this.lastVerifiedAt.getTime();
    return timeDiff > maxAge;
  }

  /**
   * 检查是否应该重试操作
   * 
   * 业务逻辑：
   * 1. 检查账号状态是否为'error'
   * 2. 检查重试次数是否小于最大重试次数
   * 3. 两个条件都满足才应该重试
   * 
   * @param maxRetryCount 最大重试次数，默认3次
   * @returns boolean 是否应该重试
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.status = 'error';
   * account.retryCount = 2;
   * console.log(account.shouldRetry()); // true
   * ```
   */
  shouldRetry(maxRetryCount: number = DEFAULT_MAX_RETRY_COUNT): boolean {
    return this.status === 'error' && this.retryCount < maxRetryCount;
  }

  /**
   * 更新验证时间
   * 
   * 业务逻辑：
   * 1. 设置最后验证时间为当前时间
   * 2. 更新记录的最后修改时间
   * 3. 用于标记账号验证操作的完成
   * 
   * @returns void 无返回值，直接修改实体属性
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.updateVerificationTime();
   * console.log(account.lastVerifiedAt); // 当前时间
   * ```
   */
  updateVerificationTime(): void {
    this.lastVerifiedAt = new Date();
    this.updatedAt = new Date();
  }

  /**
   * 更新同步时间
   * 
   * 业务逻辑：
   * 1. 设置最后同步时间为当前时间
   * 2. 更新记录的最后修改时间
   * 3. 用于标记数据同步操作的完成
   * 
   * @returns void 无返回值，直接修改实体属性
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.updateSyncTime();
   * console.log(account.lastSyncedAt); // 当前时间
   * ```
   */
  updateSyncTime(): void {
    this.lastSyncedAt = new Date();
    this.updatedAt = new Date();
  }

  /**
   * 设置错误状态
   * 
   * 业务逻辑：
   * 1. 将账号状态设置为'error'
   * 2. 记录具体的错误信息
   * 3. 增加重试计数器
   * 4. 更新最后修改时间
   * 
   * @param errorMessage 错误信息，描述具体的错误原因
   * @returns void 无返回值，直接修改实体属性
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.setError('API连接超时');
   * console.log(account.status); // 'error'
   * console.log(account.retryCount); // 增加1
   * ```
   */
  setError(errorMessage: string): void {
    this.status = 'error';
    this.errorMessage = errorMessage;
    this.retryCount += 1;
    this.updatedAt = new Date();
  }

  /**
   * 清除错误状态
   * 
   * 业务逻辑：
   * 1. 检查当前状态是否为'error'
   * 2. 如果是错误状态，恢复为'active'状态
   * 3. 清空错误信息
   * 4. 更新最后修改时间
   * 
   * @returns void 无返回值，直接修改实体属性
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.status = 'error';
   * account.clearError();
   * console.log(account.status); // 'active'
   * console.log(account.errorMessage); // null
   * ```
   */
  clearError(): void {
    if (this.status === 'error') {
      this.status = 'active';
      this.errorMessage = null;
      this.updatedAt = new Date();
    }
  }

  /**
   * 重置重试计数
   * 
   * 业务逻辑：
   * 1. 将重试次数重置为0
   * 2. 更新最后修改时间
   * 3. 用于成功操作后清除重试记录
   * 
   * @returns void 无返回值，直接修改实体属性
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.retryCount = 3;
   * account.resetRetryCount();
   * console.log(account.retryCount); // 0
   * ```
   */
  resetRetryCount(): void {
    this.retryCount = 0;
    this.updatedAt = new Date();
  }

  /**
   * 激活账号
   * 
   * 业务逻辑：
   * 1. 将账号状态设置为'active'
   * 2. 清空错误信息
   * 3. 重置重试计数为0
   * 4. 更新最后修改时间
   * 
   * @returns void 无返回值，直接修改实体属性
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.status = 'suspended';
   * account.activate();
   * console.log(account.status); // 'active'
   * ```
   */
  activate(): void {
    this.status = 'active';
    this.errorMessage = null;
    this.retryCount = 0;
    this.updatedAt = new Date();
  }

  /**
   * 暂停账号
   * 
   * 业务逻辑：
   * 1. 将账号状态设置为'suspended'
   * 2. 如果提供了原因，记录到错误信息中
   * 3. 更新最后修改时间
   * 
   * @param reason 暂停原因，可选参数，用于记录暂停的具体原因
   * @returns void 无返回值，直接修改实体属性
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.suspend('违反使用规则');
   * console.log(account.status); // 'suspended'
   * console.log(account.errorMessage); // '违反使用规则'
   * ```
   */
  suspend(reason?: string): void {
    this.status = 'suspended';
    if (reason) {
      this.errorMessage = reason;
    }
    this.updatedAt = new Date();
  }

  /**
   * 停用账号
   * 
   * 业务逻辑：
   * 1. 将账号状态设置为'inactive'
   * 2. 更新最后修改时间
   * 3. 用于临时停用账号但保留数据
   * 
   * @returns void 无返回值，直接修改实体属性
   * 
   * @example
   * ```typescript
   * const account = new ZulipAccounts();
   * account.deactivate();
   * console.log(account.status); // 'inactive'
   * ```
   */
  deactivate(): void {
    this.status = 'inactive';
    this.updatedAt = new Date();
  }
}