<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Contracts\Repositories\AdminRepositoryInterface;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Auth\TwoFactorCodeRequest;
use App\Services\AdminAuthenticationService;
use App\Services\TwoFactorService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\View\View;

class TwoFactorController extends Controller
{
    public function challenge(Request $request): View|RedirectResponse
    {
        if (! $request->session()->has('admin_2fa_id')) {
            return redirect()->route('admin.login');
        }

        return view('admin.auth.two-factor-challenge');
    }

    public function verify(
        TwoFactorCodeRequest $request,
        AdminRepositoryInterface $admins,
        TwoFactorService $twoFactor,
        AdminAuthenticationService $authentication,
    ): RedirectResponse {
        $key = 'admin-2fa|'.$request->ip().'|'.$request->session()->get('admin_2fa_id');
        if (RateLimiter::tooManyAttempts($key, config('security.two_factor.max_attempts'))) {
            return back()->withErrors(['code' => __('Too many verification attempts. Please sign in again.')]);
        }

        $admin = $admins->findById((int) $request->session()->get('admin_2fa_id'));
        $expired = time() > (int) $request->session()->get('admin_2fa_expires_at', 0);

        $code = $request->string('code')->toString();
        $verified = $admin !== null && ! $expired && (
            $twoFactor->verify($admin->two_factor_secret, $code)
            || $twoFactor->consumeRecoveryCode($admin, $code)
        );

        if ($admin === null || ! $admin->is_active || ! $verified) {
            RateLimiter::hit($key, config('security.two_factor.decay_seconds'));

            return back()->withErrors(['code' => __('The authentication code is invalid or expired.')]);
        }

        $remember = (bool) $request->session()->pull('admin_2fa_remember', false);
        $request->session()->forget(['admin_2fa_id', 'admin_2fa_expires_at']);
        RateLimiter::clear($key);
        $authentication->completeLogin($request, $admin, $remember);

        return redirect()->intended(route('admin.dashboard'));
    }

    public function setup(Request $request, TwoFactorService $twoFactor): View
    {
        $secret = $twoFactor->createSecret();
        $request->session()->put('admin_2fa_setup_secret', $secret);

        return view('admin.auth.two-factor-setup', [
            'secret' => $secret,
            'qrCodeUrl' => $twoFactor->qrCodeUrl($request->user('admin'), $secret),
        ]);
    }

    public function manage(Request $request): View
    {
        return view('admin.auth.security', ['admin' => $request->user('admin')]);
    }

    public function regenerateRecoveryCodes(Request $request, TwoFactorService $twoFactor): RedirectResponse
    {
        $request->validate(['password' => ['required', 'current_password:admin']]);
        abort_unless($request->user('admin')->hasTwoFactorEnabled(), 422);

        $recoveryCodes = $twoFactor->recoveryCodes();
        $request->user('admin')->forceFill([
            'two_factor_recovery_codes' => $twoFactor->hashRecoveryCodes($recoveryCodes),
        ])->save();

        return back()->with('recovery_codes', $recoveryCodes);
    }

    public function enable(TwoFactorCodeRequest $request, TwoFactorService $twoFactor): RedirectResponse
    {
        $secret = $request->session()->get('admin_2fa_setup_secret');
        if (! is_string($secret) || ! $twoFactor->verify($secret, $request->string('code')->toString())) {
            return back()->withErrors(['code' => __('That code is invalid or expired. Enter the current 6-digit code from your authenticator app.')]);
        }

        $request->session()->forget('admin_2fa_setup_secret');
        $recoveryCodes = $twoFactor->recoveryCodes();
        $request->user('admin')->forceFill([
            'two_factor_secret' => $secret,
            'two_factor_recovery_codes' => $twoFactor->hashRecoveryCodes($recoveryCodes),
            'two_factor_confirmed_at' => now(),
        ])->save();

        return redirect()->route('admin.dashboard')->with('status', __('Two-factor authentication is enabled. Store your recovery codes securely: :codes', ['codes' => implode(', ', $recoveryCodes)]));
    }

    public function disable(Request $request): RedirectResponse
    {
        $request->validate(['password' => ['required', 'current_password:admin']]);
        $request->user('admin')->forceFill([
            'two_factor_secret' => null,
            'two_factor_recovery_codes' => null,
            'two_factor_confirmed_at' => null,
        ])->save();

        return back()->with('status', __('Two-factor authentication is disabled.'));
    }
}
