<?php

namespace apexl\Io\modules\user\services\twoFactorAuth\methods;

use apexl\Io\modules\user\services\currentUser;
use apexl\Io\modules\userDisplay\components\twoFactorAuth\Totp2FaTiles;
use apexl\Io\services\Logger;
use RobThree\Auth\Providers\Qr\BaconQrCodeProvider;
use RobThree\Auth\TwoFactorAuth;
use Psr\Http\Message\ServerRequestInterface as Request;

class Totp2FaMethod extends Abstract2FaMethod
{
    public static function getMethodName()
    {
        return 'totp';
    }
    
    public function setup()
    {
        $renderer = new BaconQrCodeProvider();
        
        $tfa = new TwoFactorAuth(
            $this->twoFactorAuthService->getAppName(),
            6,
            30,
            'sha1',
            $renderer
        );

        // 1. Generate the secret
        $secret = $tfa->createSecret();
        header('2fa-secret: ' . $secret);

        // 2. Create the Data URI string locally
        $qrCode = $tfa->getQRCodeImageAsDataUri($this->currentUser->email, $secret);

        // Store token in DB
        $this->twoFactorAuthService->store2FaMethod(self::getMethodName(), $this->currentUser->id, $secret);

        // 3. Return this string in your JSON response
        return Totp2FaTiles::setupTile([
            'qrCode' => $qrCode,
            'secret' => $secret
        ]);
    }

    public function processSetup($providedCode)
    {

        $twoFactorMethodEntity = $this->twoFactorAuthService->retrievePending2FaMethod(self::getMethodName(), $this->currentUser->id);

        if ($twoFactorMethodEntity->isLocked()) {
            return (object)[
                'isValid' => false,
                'error' => 'Two Factor Authentication setup has been blocked temporarily due to to many failed authentication attempts. Please wait 15 minutes before trying again.',
            ];
        }

        // Initialize using the PHP 7.4 compatible 1.8.2 setup
        $tfa = new TwoFactorAuth($this->twoFactorAuthService->getAppName());
//$providedCode = '896827';
        $decrypted = $twoFactorMethodEntity->getSecret();

//print_r($twoFactorMethodEntity->getData());

// Log these to your system debug log
  //      echo "<br>DECRYPTED SECRET: " . $decrypted;
  //      echo "<br>EXPECTED CODE RIGHT NOW: " . $tfa->getCode($decrypted);
   //     echo "<br>PROVIDED CODE BY USER: " . $providedCode;

        // Verify the code with a discrepancy of 1 (allows 30 seconds of clock drift)
        $isValid = $tfa->verifyCode($twoFactorMethodEntity->getSecret(), $providedCode, 4);

        $this->twoFactorAuthService->log('check_setup_code_validity', [
            'expected_code' => $tfa->getCode($decrypted),
            'provided_code' => $providedCode,
            'isValid' => $isValid,
        ]);

        if ($isValid) {
            $twoFactorMethodEntity->is_active = 1;
            $twoFactorMethodEntity->store();
            $recoveryCodes = $this->enable();
        } else {
            $this->processFailedAttempt($twoFactorMethodEntity);
        }

        return (object)[
            'isValid' => $isValid,
            'recoveryCodes' => $recoveryCodes ?? [],
        ];
    }

    public function authenticate()
    {
        return Totp2FaTiles::authenticateTile();
    }

    public function processAuthentication(Request $request, $providedCode)
    {
        $currentUser = currentUser::getCurrentUser();

        // Enforce strict string formatting for leading zeros (e.g., "012345")
        $providedCode = str_pad(trim((string)$providedCode), 6, '0', STR_PAD_LEFT);

        // Fetch the ACTIVE TOTP method entity for this user
        $twoFactorMethodEntity = $this->twoFactorAuthService->retrieveActive2FaMethod($currentUser->id);

        if ($twoFactorMethodEntity->isLocked()) {
            return (object)[
                'isValid' => false,
                'error' => 'Your account has been locked temporarily due to to many failed authentication attempts. Please wait 15 minutes before trying again.',
            ];
        }

        // Decrypt the secret back to raw Base32
        $decrypted = $twoFactorMethodEntity->getSecret();

        // Initialize the TOTP library
        $tfa = new TwoFactorAuth($this->twoFactorAuthService->getAppName());

        // 6. Verify the code (Discrepancy 1 allows 30 seconds of clock drift)
        $isValid = $tfa->verifyCode($decrypted, $providedCode, 4);

        $this->twoFactorAuthService->log('check_authenticate_code_validity', [
            "EXPECTED CODE RIGHT NOW" => $tfa->getCode($decrypted),
            "PROVIDED CODE BY USER" => $providedCode,
            "IS VALID" => $isValid,
        ]);

        if ($isValid) {
            currentUser::addSessionData($request, ['twoFaPassed' => true]);
        } else {
            $this->processFailedAttempt($twoFactorMethodEntity);
        }

        return (object)[
            'isValid' => $isValid,
            'error' => null,
        ];
    }
}