<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\User;
use App\Notifications\TwoFactorCode;
use App\Support\TwoFactor;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;

class TwoFactorController extends Controller
{
    /**
     * The login challenge shown after a correct password when the account has
     * 2FA enabled. The user is NOT yet authenticated at this point — the
     * pending user id lives in the session.
     */
    public function show(Request $request)
    {
        $pendingId = $request->session()->get('2fa:user');

        if (!$pendingId || !($user = User::find($pendingId))) {
            return redirect()->route('login');
        }

        if ($user->two_factor_method === 'email') {
            $this->ensureEmailCodeSent($user);
        }

        return view('auth.two-factor', ['method' => $user->two_factor_method]);
    }

    public function verify(Request $request)
    {
        $request->validate(['code' => ['required', 'string', 'max:10']]);

        $pendingId = $request->session()->get('2fa:user');

        if (!$pendingId || !($user = User::find($pendingId))) {
            return redirect()->route('login');
        }

        if (!$this->codeValid($user, $request->input('code'))) {
            return back()->withErrors(['code' => 'That verification code is invalid or has expired.']);
        }

        $remember = (bool) $request->session()->pull('2fa:remember', false);
        $request->session()->forget(['2fa:user', '2fa:email_code', '2fa:email_expires']);
        Cache::forget('2fa:email:'.$user->id);

        Auth::login($user, $remember);
        $request->session()->regenerate();

        $user->update([
            'last_login_ip' => ip(),
            'last_seen_at' => now(),
        ]);

        return redirect()->intended($user->is_admin ? '/admin/home' : '/dashboard');
    }

    public function resend(Request $request)
    {
        $pendingId = $request->session()->get('2fa:user');

        if (!$pendingId || !($user = User::find($pendingId))) {
            return redirect()->route('login');
        }

        if ($user->two_factor_method === 'email') {
            $this->sendEmailCode($user);

            return back()->with('success', 'A new code has been emailed to you.');
        }

        return back();
    }

    private function codeValid(User $user, string $code): bool
    {
        if ($user->two_factor_method === 'totp') {
            return $user->two_factor_secret && TwoFactor::verify($user->two_factor_secret, $code);
        }

        $expected = Cache::get('2fa:email:'.$user->id);

        return $expected && hash_equals((string) $expected, preg_replace('/\D/', '', $code));
    }

    private function ensureEmailCodeSent(User $user): void
    {
        if (!Cache::has('2fa:email:'.$user->id)) {
            $this->sendEmailCode($user);
        }
    }

    private function sendEmailCode(User $user): void
    {
        $code = TwoFactor::numericCode();
        Cache::put('2fa:email:'.$user->id, $code, now()->addMinutes(10));

        try {
            $user->notify(new TwoFactorCode($code));
        } catch (\Throwable $exception) {
            // Fail-soft: the code is still valid from cache; log for debugging.
            \Log::warning('2FA email send failed: '.$exception->getMessage());
        }
    }
}
