<?php

namespace App\Http\Controllers\api\v1;

use App\Models\User;
use App\Constant\OTPStatus;

use App\Services\ApiKeyService;
use App\Services\OtpService;
use Illuminate\Http\Request;
use App\Models\api\v1\ApiKey;
use Lcobucci\JWT\Exception;
use Mediumart\Orange\SMS\SMS;
use App\Models\api\v1\MfaCode;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Route;
use Mediumart\Orange\SMS\Http\SMSClient;
use Carbon\Carbon;


class MFAController extends Controller
{
    /**
     * Generate and Send Code
     */
    public function generateSendCode(Request $request): JsonResponse
    {
        try
        {
            if (!OtpService::checkIfUsernameIsRegistred($request->username, $request->action))
            {
                Log::alert("Username [".$request->username."] not found to send OTP");
                return $this->ReturnResponse(false, "Ce compte n'existe pas", [], 404);
            }

            $otp_max_interval = ApiKeyService::getApiKey($request)->otp_max_interval;

            if (OtpService::checkSendTimeOTP($request->username, $request->action, $otp_max_interval))
            {
                return $this->ReturnResponse(true, "[".$otp_max_interval."] between two messages sent for the same username", [],423);
            }

            MfaCode::where([['status', OTPStatus::REQUESTED],['recepient',$request->username],['action', $request->action]])
                ->orwhere([['status',OTPStatus::PENDING],['recepient',$request->username],['action', $request->action]])
                ->update(['status'=> OTPStatus::EXPIRED_BY_NEW_CODE]);


            $code       = rand(10000, 99999);
            $message    = "Votre code de vérification est " . $code;

            if((new OtpService())->sendSMS(
                $message,
                $request->username,
                env('SMS_CREDENTIAL_FIRST_PARAM'),
                env('SMS_CREDENTIAL_SECOND_PARAM'),
                env('SMS_FROM_FIRST_PARAM'),
                env('SMS_FROM_SECOND_PARAM')
            ))
            {
                // Faire un check sur l'envoi du SMS
                MfaCode::create([
                    'recepient'     => $request->username,
                    'code'          => $code,
                    'status'        => OTPStatus::PENDING,
                    'action'        => strtoupper(trim($request->action)),
                    'api_key_id'    => ApiKey::where('key',$request->header('x-api-key'))->first()->id,
                    'expire_at'     => now()->addSeconds(ApiKeyService::getApiKey($request)->otp_max_interval)
                ]);

                Log::info("OTP sent to [".$request->username."] - Code:  [".$code."]");

                return $this->ReturnResponse(true, "OTP Sent", []);
            }
            else
            {
                Log::alert("Otp not sent [".$request->username."]");
                return $this->ReturnResponse(false, "OTP not Sent", []);
            }
        }
        catch (Exception $ex)
        {
            Log::alert("OTP not sent [".$request->username."] - Exception: ".$ex->getMessage());
            return $this->ReturnResponse(false, "OTP not Sent", []);
        }

    }


    public function verifyCode(Request $request): JsonResponse
    {
        // Verification username
        if (!OtpService::checkIfUsernameIsRegistred($request->username, $request->action))
        {
            Log::alert("Username [".$request->username."] not found to send OTP");
            return $this->ReturnResponse(false, "OTP is not valid", [], 401);
        }

        $last_otp = MfaCode::
            orderBy('created_at','desc')
            ->where([
                        ['api_key_id',  ApiKeyService::getApiKey($request)->id],
                        ['status',      OTPStatus::PENDING],
                        ['recepient',   $request->username],
                        ['action',      $request->action]
                    ]
            )->first();


        if (!$last_otp)
        {
            Log::info("OTP is not Valid [".$request->username."]");
            return $this->ReturnResponse(false, "OTP is not valid", [], 401);
        }


        if($last_otp->code == $request->code)
        {

            if ($last_otp->expire_at <= Carbon::parse(now()))
            {
                Log::info("OTP - Username [".$request->username."] - Code:  [".$request->code."] expired");
                return $this->ReturnResponse(false, "OTP has expired", []);
            }


            $last_otp->update([
                "status"        => OTPStatus::APPROVED,
                "used_at"       => now(),
                "updated_at"    => now(),
                "used"          => 1,
            ]);
            Log::info("OTP is Valid [".$request->username."] - Code:  [".$request->code."]");
            return $this->ReturnResponse(true, "OTP is valid", []);
        }


        $last_otp->update([
            "invalidAttempts"=>  $last_otp->InvalidAttempts ++
        ]);

        
        $invalid_attemtps_count = OtpService::getInvalidAttemptsCount(ApiKeyService::getApiKey($request), $last_otp, $request->username, $request->action);

        if ($invalid_attemtps_count >= (int) ApiKeyService::getApiKey($request)->otp_max_failed_attempts)
        {
            $last_otp->update([
                "status"        => OTPStatus::DENIED,
                "updated_at"    => now()
            ]);

            $user = User::whereUsername(trim($request->username))->first();
            if ($user)
            {
                $user->update([
                    "blocked"       => true,
                    "updated_at"    => now()
                ]);
            }
        }

        Log::info("OTP is not Valid [".$request->username."] - Code:  [".$request->code."]");

        return $this->ReturnResponse(false, "OTP is not valid", [], 401);


    }

}

