/**
 * Value Object representing a credit score with range validation (0-1000).
 * Immutable with private constructor and static factory.
 */
export class CreditScore {
  private static readonly MIN = 0;
  private static readonly MAX = 1000;

  private constructor(private readonly _value: number) {}

  /**
   * Create a CreditScore from a numeric value.
   * @param value - Must be between 0 and 1000 inclusive
   */
  static create(value: number): CreditScore {
    if (!Number.isFinite(value)) {
      throw new Error('Credit score must be a finite number');
    }
    if (value < CreditScore.MIN || value > CreditScore.MAX) {
      throw new Error(`Credit score must be between ${CreditScore.MIN} and ${CreditScore.MAX}`);
    }
    const normalized = Math.round(value * 100) / 100;
    return new CreditScore(normalized);
  }

  /**
   * Reconstruct from a stored string value (e.g. from database numeric column).
   */
  static fromString(value: string): CreditScore {
    const parsed = parseFloat(value);
    if (isNaN(parsed)) {
      throw new Error('Invalid credit score string value');
    }
    return CreditScore.create(parsed);
  }

  get value(): number {
    return this._value;
  }

  /**
   * Returns the letter grade for the credit score.
   * A: 80-100, B: 60-79, C: 40-59, D: 20-39, F: <20
   * (Mapped to 0-100 scale from 0-1000)
   */
  get level(): string {
    const scaled = this._value / 10; // Convert 0-1000 to 0-100
    if (scaled >= 80) return 'A';
    if (scaled >= 60) return 'B';
    if (scaled >= 40) return 'C';
    if (scaled >= 20) return 'D';
    return 'F';
  }

  /**
   * Returns the string representation suitable for database storage.
   */
  toStorageString(): string {
    return this._value.toFixed(2);
  }

  toString(): string {
    return `${this._value.toFixed(2)} (${this.level})`;
  }

  equals(other: CreditScore): boolean {
    return this._value === other._value;
  }

  isAbove(threshold: number): boolean {
    return this._value > threshold;
  }

  isBelow(threshold: number): boolean {
    return this._value < threshold;
  }
}
