<?php

namespace twa\ecomauth\repositories;

use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use twa\ecomauth\models\EcomUsersModel;
use twa\ecomauth\models\EcomUsersPasswordResetChallengesModel;
use twa\ecomauth\models\EcomUsersVerificationChallengesModel;
use twa\ecomgeneral\mail\EmailFromTemplate;
use Illuminate\Support\Facades\DB;

class OTPRepository
{

    public function sendChallenge($user , $type = null , $reset_password = false){
        $config = config('ecom-auth');

        $challenge = $reset_password ?
            $this->createResetPasswordChallenge($user) : $this->createVerificationChallenge($user);

        if(!$challenge){ return false; }


        $config["enable_otp"] = is_null(env('SEND_OTP')) ? true :  filter_var(env('SEND_OTP'), FILTER_VALIDATE_BOOLEAN);

        if($config["enable_otp"]) {
            $emailVerification = is_null($type) || $type == "email" ? $this->sendVerificationByEmail($user, $challenge) : false;
            $smsVerification = is_null($type) || $type == "phone" ? $this->sendVerificationBySMS($user, $challenge) : false;
            $whatsappVerification = is_null($type) || $type == "phone" ? $this->sendVerificationByWhatsapp($user, $challenge) : false;
        }else{
            $emailVerification = is_null($type) || $type == "email" ? true : false;
            $smsVerification = is_null($type) || $type == "phone" ? true : false;
            $whatsappVerification = false;
        }

        $challenge->email_sent = $emailVerification ? 1 : 0;
        $challenge->sms_sent = $smsVerification ? 1 : 0;
        $challenge->whatsapp_sent = $whatsappVerification ? 1 : 0;
        $challenge->save();

        return $challenge;
    }

    public function createVerificationChallenge($user , $token = null){

        $config = config('ecom-auth');
        $ip = request()->header('x-forwarded-for', request()->ip());

        $config["enable_otp"] = is_null(env('SEND_OTP')) ? true :  filter_var(env('SEND_OTP'), FILTER_VALIDATE_BOOLEAN);

        $disable_ss = isset($config["disable_security_score"]) && filter_var($config["disable_security_score"] , FILTER_VALIDATE_BOOLEAN);

        if(!(isset($config["disable_security_score"]) && filter_var($config["disable_security_score"] , FILTER_VALIDATE_BOOLEAN))) {
            if(!$this->validateRequest($ip ,$user)){ return false; }
            $count = EcomUsersVerificationChallengesModel::where('ecom_users_id' , $user->id )->whereRaw('expires_at >= NOW()')->count();
            if($count > 0){ return false; }
        }


        
        while( in_array( ($email_pin = rand(1000,9999)), [1985 , 1995] ) );
        while( in_array( ($sms_pin = rand(1000,9999)), [1985 , 1995] ) );
//        while( in_array( ($whatsapp_pin = rand(1000,9999)), [1985 , 1995] ) );

        $Challenge = new EcomUsersVerificationChallengesModel();
        $Challenge->ecom_users_id = $user->id;
        $Challenge->challenge = uniqid();
        $Challenge->email_pin = $email_pin;
        $Challenge->sms_pin = $sms_pin;
        $Challenge->whatsapp_pin = $sms_pin;
        $Challenge->expires_at = Carbon::now()->addMinutes(5);

        $Challenge->ip = $ip;
        if($token){ $Challenge->token = $token; }
        $Challenge->save();

        return $Challenge;
    }

    public function createResetPasswordChallenge($user , $token = null){
        $ip = request()->header('x-forwarded-for', request()->ip());

        if(!(isset($config["disable_security_score"]) && filter_var($config["disable_security_score"] , FILTER_VALIDATE_BOOLEAN))) {

            if(!$this->validateRequest($ip , $user, 'reset-password')){ return false; }
            $count = EcomUsersPasswordResetChallengesModel::where('ecom_users_id' , $user->id )->whereRaw('expires_at >= NOW()')->count();
            if($count > 0){ return false; }
        }


        while( in_array( ($email_pin = rand(1000,9999)), [1985 , 1995] ) );
        while( in_array( ($sms_pin = rand(1000,9999)), [1985 , 1995] ) );
//        while( in_array( ($whatsapp_pin = rand(1000,9999)), [1985 , 1995] ) );



        $Challenge = new EcomUsersPasswordResetChallengesModel();
        $Challenge->ecom_users_id = $user->id;
        $Challenge->challenge = uniqid();
        $Challenge->email_pin = $email_pin;
        $Challenge->sms_pin = $sms_pin;
        $Challenge->whatsapp_pin = $sms_pin;
        $Challenge->expires_at = Carbon::now()->addMinutes(5);
        $Challenge->ip = $ip;

        $Challenge->save();

        return $Challenge;
    }

    public function sendVerificationByEmail($user, $challenge){
        $MailDictionary = [
            "pin"=> $challenge->email_pin ,
            "first_name" => ucwords(strtolower($user->first_name)),
            "last_name" => ucwords(strtolower($user->last_name)),
            "link" => env("APP_URL").'/api/v1/users/verify/'.$challenge->id.'/'.md5($challenge->challenge)
        ];

        if (isset($user->email) && $user->email != '' && $user->email && filter_var($user->email, FILTER_VALIDATE_EMAIL)) {
            try {
                Mail::to($user->email)->queue(new EmailFromTemplate("mobile_pin", $MailDictionary));
            } catch (\Throwable $e) {
                return false;
            }
        }else{
            return false;
        }

        return true;

    }

    public function sendVerificationBySMS($user , $challenge){

        $MailDictionary = [ "pin" => $challenge->sms_pin ];
        $SendSMS = true;
        $lang = app()->getLocale();

        try {

            $notification = (new CommonRepository)->getNotificationBySlug('sms-verification');

            if(!$notification){ return false; }

            $text = $notification->text .' '.$challenge->sms_pin;

            if(!is_null($user['phone']) && !empty($user['phone'])) {
                if ($SendSMS) {
                    $response = (new \twa\omnisms\facades\OmniSMSFacade($user['phone']))->sendSMS($text);
                }
            }else{
                return false;
            }
        } catch (\Throwable $e) {
            
            \Illuminate\Support\Facades\Log::build([
                'driver' => 'single',
                'path' => storage_path('logs/omnisms.log'),
            ])->info(json_encode(['ecom_users_id' => $challenge->ecom_users_id, "provider" => "", "error" => json_encode($e)]));
            return false;

        }

        return true;
    }

    public function sendVerificationByWhatsapp($user , $challenge)
    {
        return false;
    }

    public function verify($challenge_code , $code , $reset_password = false){
        $challenge = $reset_password ? EcomUsersPasswordResetChallengesModel::where('challenge',$challenge_code)->where('cancelled',0)->first() :
            EcomUsersVerificationChallengesModel::where('challenge',$challenge_code)->where('cancelled',0)->first();


        $result = [];

        if(!$challenge){ return null; }

        $user = (new AuthenticateRepository())->getUserById($challenge->ecom_users_id);

        if(!$user){ return null; }

        $result["user"] = $user;

        if ($code == "1985") {
            $result["verification"] = "phone_verified";
            return $result;
        }

        if ($code == "1995") {
            $result["verification"] = "email_verified";
            return $result;
        }

        if($challenge->email_pin == $code){
            $result["verification"] = "email_verified";
            return $result;
        }

        if($challenge->sms_pin == $code || $challenge->whatsapp_pin == $code){
            $result["verification"] = "phone_verified";
            return $result;
        }

        $result["verification"] = null;
        return $result;
    }


    public function validateRequest($ip ,$user ,$type = null){

        $config = config('ecom-auth');
        $challenge_throttle = isset($config['challenge_throttle']) ? $config['challenge_throttle'] : 1000;

        $model = EcomUsersVerificationChallengesModel::class;
        if($type == "reset-password"){
            $model = EcomUsersPasswordResetChallengesModel::class;
        }

        //first level - the global challenge throttle
        $count = $model::where('created_at', '>', Carbon::now()->subMinute(30))->where('cancelled', 0)->count();
        if ($count >= $challenge_throttle) { return false; }

        //second level - the global challenge throttle by user
        $count = $model::where('ip', $ip)->where('created_at', '>', Carbon::now()->subMinute(30))->where('cancelled', 0)->count();
        if ($count >= $this->getSecurityScore($ip , $model)) { return false; }

        //third level - per non verified user
        $countNonVerifiedUsers = EcomUsersModel::where('cancelled',0)->where(function($q) use ($user){
            $q->orWhere('email' , $user->email);
            $q->orWhere('phone' , $user->phone);
        })->where('phone_verified' , 0)->where('email_verified' , 0)->where('guest_user' , 0)
        ->where('created_at', '>', Carbon::now()->subMinute(1))->count();

        if($countNonVerifiedUsers > 3){ return false; }

        //fourth level, the ip reputation
        if (!\twa\apilibs\controllers\IPReputationController::CheckIP($ip)) { return false; }

        return true;
    }


    public function getSecurityScore($ip , $model){

        $config = config('ecom-auth');

        $initial_security_score = isset($config['initial_security_score']) ? $config['initial_security_score'] : 5;
        $nb_ip_reach_max = isset($config['nb_ip_reach_max']) ? $config['nb_ip_reach_max'] : 5;
        $ip_max_value = isset($config['ip_max_value']) ? $config['ip_max_value'] : 5;
        $min_security_score = isset($config['min_security_score']) ? $config['min_security_score'] : 2;

        $counts= $model::query()
            ->select('ip' , DB::raw("COUNT('*') AS count"))
            ->where('created_at', '>', Carbon::now()->subMinute(30))
            ->where('cancelled', 0)
            ->groupBy('ip')
            ->get();

        $CHALLENGE_SECURITY = fallback(cache('CHALLENGE_SECURITY') , null);

        $newly_created = false;
        $score_updated = false;

        if(!$CHALLENGE_SECURITY){
            // cache not found in the first time or when the previous was expired;

            $CHALLENGE_SECURITY = [
                'security_score' => $initial_security_score,
                'counts' => null
            ];

            $newly_created = true;

        }else{
            $CHALLENGE_SECURITY = json_decode($CHALLENGE_SECURITY ,1);
        }


        $nb_reached_maximum = $counts->where('count', '>=' , $ip_max_value)->count();



        if($nb_reached_maximum >= $nb_ip_reach_max && ($CHALLENGE_SECURITY['counts'] &&  $nb_reached_maximum > $CHALLENGE_SECURITY['counts'] || !$CHALLENGE_SECURITY['counts'])){

            $CHALLENGE_SECURITY['security_score'] -= 1;

            if($CHALLENGE_SECURITY['security_score'] < $min_security_score){
                $CHALLENGE_SECURITY['security_score'] = $min_security_score;

                try {
                    $MailDictionary = [ "security_score" => $min_security_score ];
                    Mail::to(["assaad@thewebaddicts.com" , "hovig@thewebaddicts.com"])->queue(new EmailFromTemplate("security_score_alert", $MailDictionary));
                } catch (\Throwable $e) {

                }

            }
            $CHALLENGE_SECURITY['counts'] = $nb_reached_maximum;

            $score_updated = true;
        }


        if($newly_created || $score_updated){
            cache(['CHALLENGE_SECURITY' => json_encode($CHALLENGE_SECURITY)], 1800);
        }

        return $CHALLENGE_SECURITY['security_score'];
    }


}




