<?php

namespace App\Actions\Fortify;

use App\Http\Responses\TwoFactorLoginResponse;
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable as FortifyRedirectIfTwoFactorAuthenticatable;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Mtc\MercuryDataModels\TwoFactorRememberedDevice;

class RedirectIfTwoFactorAuthenticatable extends FortifyRedirectIfTwoFactorAuthenticatable
{
    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  callable  $next
     * @return mixed
     */
    public function handle($request, $next)
    {
        $user = $this->validateCredentials($request);

        // Check if the user has 2FA enabled
        $hasTwoFactor = $this->userHasTwoFactorEnabled($user);

        if (!$hasTwoFactor) {
            return $next($request);
        }

        // Check if this device is remembered
        if ($this->isDeviceRemembered($request, $user)) {
            // Device is remembered, skip 2FA challenge
            return $next($request);
        }

        // Device not remembered, trigger 2FA challenge
        return $this->twoFactorChallengeResponse($request, $user);
    }

    /**
     * Check if the user has two factor authentication enabled.
     */
    protected function userHasTwoFactorEnabled($user): bool
    {
        if (!$user) {
            return false;
        }

        if (!in_array(TwoFactorAuthenticatable::class, class_uses_recursive($user))) {
            return false;
        }

        if (!$user->two_factor_secret) {
            return false;
        }

        // If confirmation is required, check that 2FA is confirmed
        if (Fortify::confirmsTwoFactorAuthentication()) {
            return !is_null($user->two_factor_confirmed_at);
        }

        return true;
    }

    /**
     * Check if the current device is remembered for the given user.
     */
    protected function isDeviceRemembered($request, $user): bool
    {
        $cookie = $request->cookie(TwoFactorLoginResponse::COOKIE_NAME);

        if (!$cookie) {
            return false;
        }

        $parts = explode('|', $cookie);

        if (count($parts) !== 2) {
            return false;
        }

        [$userId, $token] = $parts;

        if ((int) $userId !== $user->id) {
            return false;
        }

        $rememberedDevice = TwoFactorRememberedDevice::query()
            ->where('user_id', $user->id)
            ->where('token', hash('sha256', $token))
            ->where('expires_at', '>', now())
            ->first();

        return $rememberedDevice !== null;
    }
}
