<?php

namespace App\Modules\Admin\Account;

use App\Models\AdminUser;
use App\Jobs\Admin\ProcessEmailVerificationCode;
use App\Jobs\Admin\ProcessSendEmailForAdminEmailUpdateVerification;
use App\Modules\SMS\SendSMSViaTwilio;
use App\Modules\RemoteStorage\GoogleBucketGateway;
use App\Traits\GenerateCodeTraits;
use App\Traits\MobileNumberValidatorTraits;
use App\Traits\SendSMSTraits;
use App\Traits\ValidatorTraits;
use App\Traits\GoogleBucket;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Intervention\Image\Facades\Image;
use Illuminate\Support\Str;
use App\Http\Resources\Admin\Account as ProfileResource;
class Account
{
    use ValidatorTraits, MobileNumberValidatorTraits, SendSMSTraits, GenerateCodeTraits, GoogleBucket;

    public $googleBucket;
    public $twilio;
    public $userId;

    public function __construct(GoogleBucketGateway $googleBucket)
    {
        $this->googleBucket = $googleBucket;
        $this->twilio = new SendSMSViaTwilio;
        $this->userData = Auth::user();
    }

    public function fetchAccountDetails()
    {
    
        $adminData = ProfileResource::collection(
            AdminUser::select(
                'conx_users.id',
                'conx_users.email_address',
                'conx_users.first_name',
                'conx_users.last_name',
                'conx_users.is_active',
                'conx_users.role',
                'conx_users.phone_num',
                'conx_users.enable_2fa',
                'conx_users.profile_picture',
            )
                ->where('conx_users.id', $this->userData->id)
                ->get()
            );
        
            return response()->json($adminData[0]);
    }

    public function updateAccountDetails($payload)
    {
        $userId = $this->userData->id;
        /*========================================================================================================
        Validate Query Param
        ========================================================================================================*/
        $rules = array(
            'email_address' => 'required|unique:conx_users,email_address,' . $userId . '|max:191',
            'first_name' => 'required|max:50',
            'last_name' => 'required|max:50',
            'role' => 'required',
            'phone_num' => 'required',
            'enable_2fa' => 'required',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }
        $phone_num = $this->databaseFormatNumber($payload->phone_num);

        $validate = $this->validatePhoneNumber($phone_num);
        if ($validate) {
            return $validate;
        }

        DB::beginTransaction();

        $email_address = strtolower($payload->email_address); //new email
        $first_name = ucfirst($payload->first_name); 
        $last_name = ucfirst($payload->last_name);
        $role = $payload->role;
        $is_active = true;
        $enable_2fa = $payload->enable_2fa;


        $profilePicture = $payload->profile_picture;
        $hasProfile = $profilePicture == 'null' || $profilePicture == ''?  false : true;
        $profilePicture = $hasProfile? $payload->profile_picture : null;
        if ($hasProfile) {

            $isBase64Profile = $payload->isBase64Profile == false || $payload->isBase64Profile == 'false'? false : true;
            if(!$isBase64Profile){
                $this->removeUploadedFile($payload->previousProfileLink, $this->googleBucket); //remove the previous link
            }

            $image_64 = $profilePicture;
            $extension = explode('/', explode(':', substr($image_64, 0, strpos($image_64, ';')))[1])[1]; // .jpg .png
            $image = explode(',', $image_64)[1];
            $filePath = $this->createUniqueImageNameAdmin($extension);
            $this->uploadBase64Image($image, $filePath, $this->googleBucket);

            $profilePicture = $filePath;
        }
        /**
         * CHECK IF EMAIL IS BEING UPDATED,
         */

        $is_email_updated = false;
        $current_email = Auth::user()->email_address; 
        if ($current_email != $email_address) {
            $user = AdminUser::where('email_address', $email_address)->first();
            $is_email_updated = true; //true if email is updated
        }

        /*========================================================================================================
        preparing user data to be saved on database
        ========================================================================================================*/
        if ($is_email_updated) {
            $token = hash('sha256', $plainTextToken = Str::random(80));
            $user_data = array(
                'email_address' => $email_address,
                'first_name' => $first_name,
                'last_name' => $last_name,
                'role' => $role,
                'phone_num' => $phone_num,
                'enable_2fa' => $enable_2fa,
                'email_verification_code' => $token,
                'new_email_address' => null,
                'mobile_code' => null,
                'email_code' => null,
            );
        } else {
            $user_data = array(
                'first_name' => $first_name,
                'last_name' => $last_name,
                'role' => $role,
                'phone_num' => $phone_num,
                'enable_2fa' => $enable_2fa,
            );
        }

        if($hasProfile){
            $user_data['profile_picture'] = $profilePicture; //add profile link
        }

        /*========================================================================================================
        update user data to conx_users table
        ========================================================================================================*/
        $adminUserTransaction = $this->updateAdminUser($userId, $user_data);

        /*========================================================================================================
        If there's an error or queries don't do their job, rollback!
        ========================================================================================================*/

        if (!$adminUserTransaction) {
            DB::rollBack();
            return response()->json(['error' => 'Server error.']);
        } else {
            if ($is_email_updated) {
                $user_data = [
                    'email_address' => $email_address,
                    'first_name' => $first_name,
                    'last_name' => $last_name,
                    'code' => $token,
                ];
                $email_verification = $this->sendEmailVerification($email_address, $user_data);
            }
            DB::commit();
            return response()->json(['success' => 'Data updated successfully.'])->setStatusCode(201);
        }
    }

    public function sendEmailVerification($email_address, $user_data)
    {
        return ProcessSendEmailForAdminEmailUpdateVerification::dispatch($email_address, $user_data)->onConnection('jobs_emails')->onQueue('sendemail');
    }

    public function verifyCurrentMobile()
    {

        $recevierNumber = $this->userData->phone_num;

        //Validate the saved number if valid
        $validate = $this->validatePhoneNumber($recevierNumber);
        if ($validate) {
            return $validate;
        }

        $newlyGenaratedCode = strtoupper($this->generateRandomSixDigitCode());
        $message = '[ConX] Verification Code: ' . $newlyGenaratedCode;

        $sendSMSTransaction = $this->twilio->sendSMS($recevierNumber, $message);
        $savedCodeTransaction = $this->updateAdminUser($this->userData->id, ['mobile_code' => $newlyGenaratedCode]);

        if ($sendSMSTransaction && $savedCodeTransaction) {
            return response()->json(['success' => 'We have sent a verification code to your mobile number. Please check your inbox or spam folder to verify it.']);
        } else {
            return response()->json(['error' => 'Something went wrong while sending verification code. Please try again later']);
        }
    }

    public function verifyNewMobileAndEmailAddress($payload)
    {
        $userId = $this->userData->id;

        $rules = array(
            'email_address' => 'required|unique:conx_users,email_address,' . $userId . '|max:191',
            'phone_number' => 'required',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        $recevierNumber = "+1" . str_replace(array('-', '(', ')', ' '), '', $payload->phone_number);

        $validate = $this->validatePhoneNumber($recevierNumber);
        if ($validate) {
            return $validate;
        }

        $emailGenaratedCode = strtoupper($this->get_rand_alphanumeric());
        $mobileGenaratedCode = strtoupper($this->get_rand_alphanumeric());
        $smsMessage = '[ConX] Verification Code: ' . $mobileGenaratedCode;

        $userDataForEmail = [
            'email_address' => $this->userData->email_address,
            'first_name' => $this->userData->first_name,
            'last_name' => $this->userData->last_name,
            'code' => $emailGenaratedCode,
        ];

        $newEmailAddress = strtolower($payload->email_address);
       
        $emailTranscation = $this->sendEmailVerificationCode($newEmailAddress, $userDataForEmail);
        $sendSMSTransaction = $this->twilio->sendSMS($recevierNumber, $smsMessage);
        $savedCodeTransaction = $this->updateAdminUser($this->userData->id, ['email_code' => $emailGenaratedCode, 'mobile_code' => $mobileGenaratedCode]);

        if ($emailTranscation && $sendSMSTransaction && $savedCodeTransaction) {
            return response()->json(['success' => ' We have sent a verification code to your updated email address and mobile number. Please check your inbox or spam folder to verify it.']);
        } else {
            return response()->json(['errors' => 'Something went wrong while sending verification code. Please try again later']);
        }
    }

    public function verifyUpdatedMobile($payload)
    {

        //Validate phone number
        $rules = array(
            'phone_number' => 'required',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        $validate = $this->validatePhoneNumber($payload->phone_number);
        if ($validate) {
            return $validate;
        }

        $recevierNumber = "+1" . str_replace(array('-', '(', ')', ' '), '', $payload->phone_number);
        $newlyGenaratedCode = strtoupper($this->generateRandomSixDigitCode());
        $message = '[ConX] Verification Code: ' . $newlyGenaratedCode;

        $sendSMSTransaction = $this->twilio->sendSMS($recevierNumber, $message);
        $savedCodeTransaction = $this->updateAdminUser($this->userData->id, ['mobile_code' => $newlyGenaratedCode]);

        if ($sendSMSTransaction && $savedCodeTransaction) {
            return response()->json(['success' => 'We have sent a verification code to your updated mobile number. Please check your inbox or spam folder to verify it.']);
        } else {
            return response()->json(['errors' => 'Something went wrong while sending verification code. Please try again later']);
        }
    }

    public function updateAdminUser($userId, $data)
    {
        return AdminUser::where('id', $userId)->update($data);
    }

    public function disableTwoFactor()
    {
        $updateTransaction = $this->updateAdminUser($this->userData->id, ['enable_2fa' => 'false']);

        if ($updateTransaction) {
            return response()->json(['success' => 'Two Factor Authentication Successfully Disabled']);
        } else {
            return response()->json(['errors' => 'Something went wrong while disabling your two factor authentication. Please try again later']);
        }
    }

    public function enableTwoFactor()
    {
        $updateTransaction = $this->updateAdminUser($this->userData->id, ['enable_2fa' => 'true']);
        if ($updateTransaction) {
            return response()->json(['success' => 'Two Factor Authentication Successfully Enabled']);
        } else {
            return response()->json(['errors' => 'Something went wrong while enabling your two factor authentication. Please try again later']);
        }
    }

    public function resendEmailVerificationCode()
    {
        $newlyGenaratedCode = strtoupper($this->get_rand_alphanumeric());
        $user_data = [
            'email_address' => $this->userData->email_address,
            'first_name' => $this->userData->first_name,
            'last_name' => $this->userData->last_name,
            'code' => $newlyGenaratedCode,
        ];

        $emailTranscation = $this->sendEmailVerificationCode($this->userData->email_address, $user_data);
        $savedCodeTransaction = $this->updateAdminUser($this->userData->id, ['email_code' => $newlyGenaratedCode]);

        if ($emailTranscation && $savedCodeTransaction) {
            return response()->json(['success' => 'We have sent a verification code to your updated email address. Please check your inbox or spam folder to verify it', 'email_code' => $newlyGenaratedCode]);
        } else {
            return response()->json(['errors' => 'Something went wrong while sending verification code. Please try again later']);
        }
    }

    public function sendEmailVerificationCode($email_address, $user_data)
    {
        return ProcessEmailVerificationCode::dispatch($email_address, $user_data)->onConnection('jobs_emails')->onQueue('sendemail');
    }

    public function verifyUpdatedEmailAddress($payload)
    {
        $rules = array(
            'email_address' => 'required|unique:conx_users,email_address,' . $this->userData->id . '|max:191',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        $newlyGenaratedCode = strtoupper($this->get_rand_alphanumeric());

        $userDataForEmail = [
            'email_address' => $this->userData->email_address,
            'first_name' => $this->userData->first_name,
            'last_name' => $this->userData->last_name,
            'code' => $newlyGenaratedCode,
        ];

        $newEmailAddress = strtolower($payload->email_address); //new email to verify

        $emailTranscation = $this->sendEmailVerificationCode($newEmailAddress, $userDataForEmail);

        $savedCodeTransaction = $this->updateAdminUser($this->userData->id, ['email_code' => $newlyGenaratedCode]);

        if ($emailTranscation && $savedCodeTransaction) {
            return response()->json(['success' => 'We have sent a verification code to your updated email address. Please check your inbox or spam folder to verify it.']);
        } else {
            return response()->json(['errors' => 'Something went wrong while sending verification code. Please try again later']);
        }
    }

    public function verificationNewMobileAndEmaillAddress($payload){

        $rules = array(
            'emailVerificationCode' => 'required',
            'email_address' => 'required',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        $inputtedMobileCode = $payload->mobileVerificationCode;
        $inputteEmailCode = $payload->emailVerificationCode;

        $mobileCodeValidation = $inputtedMobileCode? false: true;
        $emailCodeValidation =  false;

        if($inputtedMobileCode){
            $mobileCodeValidation = AdminUser::where('mobile_code', $inputtedMobileCode)->first();

            if(!$mobileCodeValidation){
                return response()->json(['errors' => 'The inputted mobile verification code is invalid.']);
            }
        }

        $emailCodeValidation = AdminUser::where('email_code', $inputteEmailCode)->first();

        if(!$emailCodeValidation){
            return response()->json(['errors' => 'The inputted email verification code is invalid.']);
        }

        $userData = array(
            'email_address' => strtolower($payload->email_address),
            'phone_num' => "+1" . str_replace(array('-', '(', ')', ' '), '', $payload->phone_num),
            'first_name' => $payload->first_name,
            'last_name' => $payload->last_name,
            'email_code' => '',
            'email_verified_at' => now(),
        );

        if($inputtedMobileCode){
            $userData['mobile_code'] = '';
        }

        $updateUserTransaction =  $this->updateAdminUser($this->userData->id, $userData);
        
        if ($updateUserTransaction) {
            return response()->json(['success' => 'Account Successfully Updated!']);
        } else {
            return response()->json(['errors' => 'Something went wrong while proccessing the verification code. Please try again later']);
        }

    }

    public function verifyMobileCode($payload){
        $rules = array(
            'mobile_code' => 'required',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        $inputtedMobileCode = $payload->mobile_code;

        $emailCodeValidation = AdminUser::where('mobile_code', $inputtedMobileCode)->update(['enable_2fa' => 'true', 'mobile_code' => null]);

        if(!$emailCodeValidation){
            return response()->json(['errors' => 'The inputted email verification code is invalid.']);
        }

        return response()->json(['success' => 'Successfully Enabled Two Factor Authentication!']); 
    }
}
