interface OtpServiceEntity {
  length?: number;
}

export class OtpService {
  private readonly otpLength: number;

  constructor({ length = 4 }: OtpServiceEntity) {
    this.otpLength = Math.max(length, 4); // Minimum of 4 digits
  }

  private hasSequentialDigits(str: string): boolean {
    for (let i = 0; i < str.length - 1; i++) {
      const current = parseInt(str[i], 10);
      const next = parseInt(str[i + 1], 10);
      if (next === current + 1 || next === current - 1) {
        return true;
      }
    }
    return false;
  }

  private hasRepeatedDigits(str: string): boolean {
    return str.split('').every((char) => char === str[0]);
  }

  private isPalindrome(str: string): boolean {
    return str === str.split('').reverse().join('');
  }

  private hasPartiallyRepeatedDigits(str: string): boolean {
    const counts: Record<string, number> = {};
    for (const char of str) {
      counts[char] = (counts[char] || 0) + 1;
    }

    // Reject if any digit appears more than twice
    return Object.values(counts).some((count) => count > 2);
  }

  private hasNoMatchLength(str: string) {
    return str.length !== this.otpLength;
  }

  private hasStartWithZero(str: string) {
    return str.split('')[0] === '0';
  }

  public generateSecureOTP(): string {
    let otp: string;

    do {
      otp = Array.from({ length: this.otpLength }, () =>
        Math.floor(Math.random() * 10).toString(),
      ).join('');
    } while (
      this.hasNoMatchLength(otp) ||
      this.hasSequentialDigits(otp) ||
      this.hasRepeatedDigits(otp) ||
      this.isPalindrome(otp) ||
      this.hasPartiallyRepeatedDigits(otp) ||
      this.hasStartWithZero(otp)
    );
    return otp;
  }
}
