/**
 * Value Object: RiskScore
 *
 * Encapsulates a risk score value (0-100) with domain validation.
 * Immutable after creation via static factory methods.
 */
export class RiskScore {
  private static readonly MIN = 0;
  private static readonly MAX = 100;
  private static readonly HIGH_THRESHOLD = 70;
  private static readonly MEDIUM_THRESHOLD = 40;

  private constructor(private readonly score: number) {}

  /**
   * Create a RiskScore from a numeric value.
   * @throws Error if score is out of range [0, 100]
   */
  static create(score: number): RiskScore {
    if (score < RiskScore.MIN || score > RiskScore.MAX) {
      throw new Error(
        `Risk score must be between ${RiskScore.MIN} and ${RiskScore.MAX}, got ${score}`,
      );
    }
    if (!Number.isFinite(score)) {
      throw new Error('Risk score must be a finite number');
    }
    return new RiskScore(score);
  }

  /**
   * Reconstitute a RiskScore from a stored string value (e.g. from DB numeric column).
   */
  static fromString(value: string): RiskScore {
    const parsed = parseFloat(value);
    if (isNaN(parsed)) {
      throw new Error(`Cannot parse risk score from string: "${value}"`);
    }
    return RiskScore.create(parsed);
  }

  /** Get the numeric value */
  get value(): number {
    return this.score;
  }

  /** Get the string representation (for DB storage) */
  toString(): string {
    return String(this.score);
  }

  /** Whether this risk score is considered high risk */
  isHighRisk(): boolean {
    return this.score >= RiskScore.HIGH_THRESHOLD;
  }

  /** Whether this risk score is considered medium risk */
  isMediumRisk(): boolean {
    return this.score >= RiskScore.MEDIUM_THRESHOLD && this.score < RiskScore.HIGH_THRESHOLD;
  }

  /** Whether this risk score is considered low risk */
  isLowRisk(): boolean {
    return this.score < RiskScore.MEDIUM_THRESHOLD;
  }

  /** Get the risk level string */
  get level(): 'high' | 'medium' | 'low' {
    if (this.isHighRisk()) return 'high';
    if (this.isMediumRisk()) return 'medium';
    return 'low';
  }

  equals(other: RiskScore): boolean {
    return this.score === other.score;
  }
}
