import * as bcrypt from 'bcrypt';

/**
 * 密码值对象
 * 包含密码验证规则和加密逻辑
 */
export class Password {
  private static readonly MIN_LENGTH = 8;
  private static readonly MAX_LENGTH = 32;
  private static readonly SALT_ROUNDS = 12;

  private constructor(public readonly hash: string) {}

  /**
   * 从明文密码创建（会进行加密）
   */
  static async create(plainPassword: string): Promise<Password> {
    Password.validatePlain(plainPassword);
    const hash = await bcrypt.hash(plainPassword, Password.SALT_ROUNDS);
    return new Password(hash);
  }

  /**
   * 从明文密码创建（跳过格式验证，用于支付密码等有独立验证规则的场景）
   */
  static async createWithoutValidation(plainPassword: string): Promise<Password> {
    const hash = await bcrypt.hash(plainPassword, Password.SALT_ROUNDS);
    return new Password(hash);
  }

  /**
   * 从已加密的 hash 重建
   */
  static fromHash(hash: string): Password {
    return new Password(hash);
  }

  /**
   * 验证明文密码是否匹配
   */
  async verify(plainPassword: string): Promise<boolean> {
    return bcrypt.compare(plainPassword, this.hash);
  }

  /**
   * 验证密码格式
   */
  private static validatePlain(password: string): void {
    if (password.length < Password.MIN_LENGTH) {
      throw new Error(`密码长度不能少于 ${Password.MIN_LENGTH} 位`);
    }
    if (password.length > Password.MAX_LENGTH) {
      throw new Error(`密码长度不能超过 ${Password.MAX_LENGTH} 位`);
    }
    // 至少包含一个字母和一个数字
    if (!/[a-zA-Z]/.test(password) || !/\d/.test(password)) {
      throw new Error('密码必须包含字母和数字');
    }
  }

  /**
   * 检查密码强度（仅验证格式，不加密）
   */
  static checkStrength(password: string): { valid: boolean; message?: string } {
    try {
      Password.validatePlain(password);
      return { valid: true };
    } catch (error) {
      return { valid: false, message: (error as Error).message };
    }
  }

  toString(): string {
    return '[PROTECTED]';
  }
}
