<?php

namespace App\Services;

class GoogleAuthenticatorService
{
    const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';

    public function generateSecret($length = 32)
    {
        $alphabet = self::BASE32_ALPHABET;
        $max = strlen($alphabet) - 1;
        $secret = '';
        for ($i = 0; $i < $length; $i++) {
            $secret .= $alphabet[random_int(0, $max)];
        }
        return $secret;
    }

    public function verifyCode($secret, $code, $window = 1, $period = 30, $digits = 6)
    {
        $code = trim((string) $code);
        if ($code === '' || !preg_match('/^\d{6}$/', $code)) {
            return false;
        }

        $timeSlice = (int) floor(time() / $period);
        for ($i = -$window; $i <= $window; $i++) {
            $calc = $this->getCode($secret, $timeSlice + $i, $digits);
            if (hash_equals($calc, $code)) {
                return true;
            }
        }
        return false;
    }

    public function getOtpAuthUrl($issuer, $account, $secret)
    {
        $label = rawurlencode($issuer . ':' . $account);
        $issuerParam = rawurlencode($issuer);
        return "otpauth://totp/{$label}?secret={$secret}&issuer={$issuerParam}&algorithm=SHA1&digits=6&period=30";
    }

    public function getQrCodeUrl($otpAuthUrl, $size = 200)
    {
        $encoded = rawurlencode($otpAuthUrl);
        return "https://api.qrserver.com/v1/create-qr-code/?size={$size}x{$size}&data={$encoded}";
    }

    protected function getCode($secret, $timeSlice, $digits)
    {
        $secretKey = $this->base32Decode($secret);
        if ($secretKey === '') {
            return '';
        }

        $time = pack('N*', 0) . pack('N*', $timeSlice);
        $hash = hash_hmac('sha1', $time, $secretKey, true);
        $offset = ord(substr($hash, -1)) & 0x0F;
        $truncatedHash = substr($hash, $offset, 4);
        $value = unpack('N', $truncatedHash)[1] & 0x7FFFFFFF;
        $modulo = pow(10, $digits);
        return str_pad((string) ($value % $modulo), $digits, '0', STR_PAD_LEFT);
    }

    protected function base32Decode($secret)
    {
        $secret = strtoupper($secret);
        $secret = preg_replace('/[^A-Z2-7]/', '', $secret);
        if ($secret === '') {
            return '';
        }

        $alphabet = array_flip(str_split(self::BASE32_ALPHABET));
        $binary = '';
        $buffer = 0;
        $bitsLeft = 0;

        foreach (str_split($secret) as $char) {
            if (!isset($alphabet[$char])) {
                continue;
            }
            $buffer = ($buffer << 5) | $alphabet[$char];
            $bitsLeft += 5;
            while ($bitsLeft >= 8) {
                $bitsLeft -= 8;
                $binary .= chr(($buffer >> $bitsLeft) & 0xFF);
            }
        }

        return $binary;
    }
}
