<?php

namespace App\Util;

/* 
Generate an OTP with an expiry for PHP without using any Database

Usage Examples:
$unique_secret = md5('user@example.com'); // Use md5 hash of the email as a unique secret
// Generate OTP
$otp = OTP::digits(8)->expiry(30)->generate($unique_secret);

// Validate OTP
$valid = OTP::digits(8)->expiry(30)->match($otp, $unique_secret);

*/
class OTP
{
    private static $digits = 4;
    private static $expiry = 10; // minutes
    private static $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    public static function digits($number)
    {
        self::$digits = $number;
        return new static();
    }

    public static function expiry($minutes)
    {
        self::$expiry = $minutes;
        return new static();
    }

    public static function strings($length)
    {
        self::$digits = $length;
        return new static();
    }

    public static function both($length)
    {
        self::$digits = $length;
        return new static();
    }

    public static function generate($unique_secret)
    {
        $otp = self::generateOtp(self::$digits);
        $expiry_time = time() + self::$expiry * 60;
        $hash = hash('sha256', $otp . $unique_secret . $expiry_time);
        return $otp . ':' . $expiry_time . ':' . $hash;
    }

    public static function check($otp_data, $unique_secret)
    {
        list($otp, $expiry_time, $hash) = explode(':', $otp_data);
        if (time() > $expiry_time) {
            return false;
        }
        $check_hash = hash('sha256', $otp . $unique_secret . $expiry_time);
        return $hash === $check_hash;
    }

    private static function generateOtp($length)
    {
        $characters = '0123456789' . self::$characters;
        $pieces = [];
        $max = strlen($characters) - 1;
        for ($i = 0; $i < $length; ++$i) {
            $pieces []= $characters[random_int(0, $max)];
        }
        return implode('', $pieces);
    }

    public static function match($otp, $unique_secret)
    {
        return self::check($otp, $unique_secret);
    }
}
