<?php

namespace App\Http\Controllers\Auth;

// Laravel Specifics
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;

// Creatia Specifics
use App\Helpers\Security;
use App\Http\Controllers\BaseController;
use App\Models\SecurityEvent;
use PragmaRX\Google2FA\Google2FA;

class MfaController extends BaseController
{
    public function status()
    {
        $user = Auth::user();

        return response()->json([
            'enabled' => $user->mfaEnabled(),
            'pending' => !$user->mfaEnabled() && $user->totp_secret !== null,
        ], 200);
    }

    public function setup(Request $_request)
    {
        try {
            $user = Auth::user();

            if($user->mfaEnabled()) {
                return response()->json(['detail' => 'MFA already enabled'], 400);
            }

            $google2fa = new Google2FA;

            $secret = $google2fa->generateSecretKey(32);

            $user->totp_secret = $secret;
            $user->totp_confirmed_at = null;
            $user->totp_last_slice = null;
            $user->save();

            return response()->json([
                'secret'      => $secret,
                'otpauth_uri' => $google2fa->getQRCodeUrl('Global Secure Layer - Creatia', $user->email, $secret),
            ], 200);
        }
        catch(\Throwable $e) {
            return response()->json(['detail' => 'Server error'], 500);
        }
    }

    public function confirm(Request $_request)
    {
        try {
            if(Validator::make($_request->input(), [
                'code' => ['required', 'digits:6'],
            ])->fails()) {
                return response()->json(['detail' => 'Invalid Payload'], 400);
            }

            $user = Auth::user();

            if($user->mfaEnabled()) {
                return response()->json(['detail' => 'MFA already enabled'], 400);
            }

            if($user->totp_secret === null) {
                return response()->json(['detail' => 'No MFA setup in progress'], 400);
            }

            $slice = (new Google2FA)->verifyKeyNewer(
                $user->totp_secret,
                $_request->input('code'),
                0,
                (int) config('security.mfa_totp_window')
            );

            if($slice === false) {
                $this->security_event($_request, 'Failed MFA code');

                return response()->json(['detail' => 'Invalid MFA code'], 400);
            }

            $user->totp_confirmed_at = Carbon::now();
            $user->totp_last_slice = $slice;
            $user->save();

            $this->security_event($_request, 'MFA enabled');

            return response()->json(['detail' => 'MFA enabled'], 200);
        }
        catch(\Throwable $e) {
            return response()->json(['detail' => 'Server error'], 500);
        }
    }

    public function disable(Request $_request)
    {
        try {
            if(Validator::make($_request->input(), [
                'code' => ['required', 'digits:6'],
            ])->fails()) {
                return response()->json(['detail' => 'Invalid Payload'], 400);
            }

            $user = Auth::user();

            if(!$user->mfaEnabled()) {
                return response()->json(['detail' => 'MFA not enabled'], 400);
            }

            $slice = (new Google2FA)->verifyKeyNewer(
                $user->totp_secret,
                $_request->input('code'),
                $user->totp_last_slice ?? 0,
                (int) config('security.mfa_totp_window')
            );

            if($slice === false) {
                $this->security_event($_request, 'Failed MFA code');

                return response()->json(['detail' => 'Invalid MFA code'], 400);
            }

            $user->totp_secret = null;
            $user->totp_confirmed_at = null;
            $user->totp_last_slice = null;
            $user->save();

            $this->security_event($_request, 'MFA disabled');

            return response()->json(['detail' => 'MFA disabled'], 200);
        }
        catch(\Throwable $e) {
            return response()->json(['detail' => 'Server error'], 500);
        }
    }

    private function security_event(Request $_request, $_description)
    {
        SecurityEvent::create([
            'user_id'       => Auth::user()->id,
            'ip'            => Security::real_ip(),
            'user_agent'    => $_request->userAgent(),
            'route'         => $_request->path(),
            'payload'       => json_encode([]),
            'description'   => $_description,
        ]);
    }
}
