<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Mail\TwoFactorOtp;
use App\Models\UserTrustedDevice;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;

class TwoFactorController extends Controller
{
    public function index()
    {
        if (! Session::has('auth.2fa_user_id')) {
            return redirect()->route('login');
        }

        // Check if device is already trusted (Redirect to verify immediately with cookie token)
        $userId = Session::get('auth.2fa_user_id');
        $deviceId = request()->cookie('2fa_trust_device');

        if ($deviceId && str_contains($deviceId, '|')) {
            $parts = explode('|', $deviceId, 2);
            if (count($parts) !== 2 || empty($parts[0]) || empty($parts[1])) {
                return view('auth.two-factor');
            }
            [$uuid, $secret] = $parts;

            $trust = UserTrustedDevice::where('user_id', $userId)
                ->where('device_id', $uuid)
                ->where('expires_at', '>', now())
                ->first();

            if ($trust && hash_equals($trust->token, hash('sha256', $secret))) {
                // Auto login and skip 2FA view
                $remember = Session::get('auth.2fa_remember', false);
                Auth::loginUsingId($userId, $remember);
                Session::forget(['auth.2fa_user_id', 'auth.2fa_code', 'auth.2fa_expires_at', 'auth.2fa_remember']);
                session()->regenerate();

                return redirect()->intended(route('dashboard', absolute: false));
            }
        }

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

    public function verify(Request $request)
    {
        $request->validate([
            'code' => 'required|string|size:6',
            'trust_device' => 'nullable|boolean',
        ]);

        $userId = Session::get('auth.2fa_user_id');
        $storedCode = Session::get('auth.2fa_code');
        $expiresAt = Session::get('auth.2fa_expires_at');

        if (! $userId || ! $storedCode || ! hash_equals((string) $storedCode, (string) $request->code)) {
            return back()->with('error', __('Invalid verification code.'));
        }

        if (! $expiresAt || now()->timestamp > $expiresAt) {
            Session::forget(['auth.2fa_user_id', 'auth.2fa_code', 'auth.2fa_expires_at', 'auth.2fa_remember']);

            return redirect()->route('login')->with('error', __('Verification code has expired. Please log in again.'));
        }

        // Handle Trust Device
        if ($request->boolean('trust_device')) {
            $this->issueTrustToken($userId);
        }

        // Login user
        $remember = Session::get('auth.2fa_remember', false);
        Auth::loginUsingId($userId, $remember);

        // Clear 2FA session then regenerate to prevent fixation
        Session::forget(['auth.2fa_user_id', 'auth.2fa_code', 'auth.2fa_expires_at', 'auth.2fa_remember']);
        session()->regenerate();

        return redirect()->intended(route('dashboard', absolute: false));
    }

    protected function issueTrustToken($userId)
    {
        $deviceId = Str::uuid();
        $token = Str::random(64);
        $days = get_setting('two_factor_trust_days', 30);

        UserTrustedDevice::create([
            'user_id' => $userId,
            'device_id' => $deviceId,
            'token' => hash('sha256', $token),
            'expires_at' => now()->addDays($days),
        ]);

        // Queue cookie with both UUID and Secret
        cookie()->queue(
            '2fa_trust_device',
            $deviceId.'|'.$token,
            $days * 24 * 60,
            null, null, true, true // secure, httpOnly
        );
    }

    public static function generateAndSendOtp($user)
    {
        $otp = str_pad((string) (hexdec(bin2hex(random_bytes(3))) % 1000000), 6, '0', STR_PAD_LEFT);
        $expiresAt = now()->addMinutes(10)->timestamp;

        Session::put('auth.2fa_user_id', $user->id);
        Session::put('auth.2fa_code', $otp);
        Session::put('auth.2fa_expires_at', $expiresAt);

        try {
            $request = request();
            Mail::to($user->email)->send(new TwoFactorOtp(
                otp: $otp,
                userName: $user->name,
                ipAddress: $request->ip(),
                userAgent: $request->userAgent(),
            ));
        } catch (\Exception $e) {
            \Log::error('Failed to send 2FA Email: '.$e->getMessage());
        }

        session()->flash('info', __('Verification code has been sent to your email.'));
    }
}
