<?php

declare(strict_types=1);

namespace Builder\Core\Http\Controllers\Account;

use Builder\Core\Http\Requests\Account\ConfirmMfaRequest;
use Illuminate\Http\RedirectResponse;
use PragmaRX\Google2FA\Google2FA;

final class ConfirmMfaController
{
    private const string SESSION_PENDING_SECRET = 'mfa_pending_secret';

    private const string SESSION_VERIFIED = 'mfa_verified';

    public function __invoke(ConfirmMfaRequest $request): RedirectResponse
    {
        $secret = $request->session()->get(self::SESSION_PENDING_SECRET);

        if (! $secret) {
            return back()->withErrors(['code' => __('builder::builder.mfa.expired_session')]);
        }

        $google2fa = new Google2FA;

        if (! $google2fa->verifyKey($secret, $request->code)) {
            return back()->withErrors(['code' => __('builder::builder.mfa.invalid_code')]);
        }

        $user = $request->user();
        assert($user !== null);

        $user->forceFill([
            'two_factor_secret' => encrypt($secret),
            'two_factor_recovery_codes' => encrypt(json_encode($this->generateRecoveryCodes())),
            'two_factor_confirmed_at' => now(),
        ])->save();

        $request->session()->forget(self::SESSION_PENDING_SECRET);
        $request->session()->put(self::SESSION_VERIFIED, true);
        $request->session()->put('show_recovery_codes', true);

        return to_route('builder.account.show')->with('success', __('builder::builder.mfa.enabled'));
    }

    /** @return list<string> */
    private function generateRecoveryCodes(): array
    {
        return array_map(
            fn () => strtoupper(bin2hex(random_bytes(4))).'-'.strtoupper(bin2hex(random_bytes(4))),
            range(1, 8),
        );
    }
}
