<?php

namespace App\Modules\Jobseeker\Resume;

use App\Models\WorkAreaLists;
use App\Models\WorkAreas;
use App\Models\Worker;
use App\Models\WorkerFile;
use App\Models\WorkerExperience;
use App\Modules\SMS\SendSMSViaTwilio;
use App\Modules\RemoteStorage\GoogleBucketGateway;
use App\Jobs\Jobseeker\ProcessEmailVerificationCode;
use App\Jobs\Jobseeker\ProcessSendEmailForNewJobseeker;
use App\Traits\GenerateCodeTraits;
use App\Traits\MobileNumberValidatorTraits;
use App\Traits\SendSMSTraits;
use App\Traits\ZipcodeTraits;
use App\Traits\ValidatorTraits;
use App\Traits\GoogleBucket;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Intervention\Image\Facades\Image;
use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;

class Resume
{
    use ValidatorTraits, MobileNumberValidatorTraits, SendSMSTraits, GenerateCodeTraits, ZipcodeTraits, GoogleBucket;
    public $twilio;
    public $googleBucket;

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

    public function updatePersonalDetails($payload)
    {
        $current_email = $this->userData->email_address;
        $jobseeker_id = $this->userData->id;
        $is_email_updated = false;
        $profilePicture = $payload->profile_picture;
        $emailAddress = strtolower($payload->email_address); //new email

        $rules = array(
            'email_address' => 'required|unique:workers,email_address,' . $jobseeker_id . '|max:191',
            'first_name' => 'required|max:50',
            'last_name' => 'required|max:50',
            'phone_num' => 'required|max:50',
            'street' => 'required|max:50',
            'city' => 'required|max:50',
            'state' => 'required|max:50',
            'zipCode' => 'required|string|min:5|max:5',
        );

        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

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

        $validate = $this->validateZipcodeIfExisting($payload->zipCode);
        if ($validate) {
            return $validate;
        }
        
        if ($current_email != $emailAddress) {
            $user = Worker::where('email_address', $emailAddress)->latest()->first();
            if ($user) {
                $response = array(
                    'errors' => 'Email address was already used.',
                    'jobseeker_id' => $jobseeker_id,
                );
                return response()->json($response)->setStatusCode(201);
            }
            $is_email_updated = true;
        }


        // 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->createUniqueImageNameJobseeker($jobseeker_id, $extension);
            $this->uploadBase64Image($image, $filePath, $this->googleBucket);

            $profilePicture = $filePath;
        }

        DB::beginTransaction();

        //if email updated, re-send verification token
        if ($is_email_updated) {
            $token = hash('sha256', $plainTextToken = Str::random(80));

            $jobseeker = [
                'email_address' => $emailAddress,
                'first_name' => ucfirst($payload->first_name),
                'last_name' => ucfirst($payload->last_name),
                'phone_num' => "+1" . str_replace(array('-', '(', ')', ' '), '', $payload->phone_num),
                'street_address' => $payload->street,
                'city' => $payload->city,
                'state' => $payload->state,
                'zipcode' => $payload->zipCode,
                'email_verification_code' => $token,
                'email_verified_at' => null,
            ];

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

            $jobseeker = tap(Worker::where('id', $jobseeker_id))->update($jobseeker)->first();

            $user_data = [
                'email_address' => $jobseeker->email_address,
                'first_name' => $jobseeker->first_name,
                'last_name' => $jobseeker->last_name,
                'email_token' => $token,
                'role' => 'jobseeker',
            ];
            $email_address = $jobseeker->email_address;
            $this->sendEmailVerification($email_address, $user_data);

        } else {

            $jobseeker = array(
                // 'email_address' => strtolower($payload->email_address),
                'first_name' => ucfirst($payload->first_name),
                'last_name' => ucfirst($payload->last_name),
                'phone_num' => "+1" . str_replace(array('-', '(', ')', ' '), '', $payload->phone_num),
                'street_address' => $payload->street,
                'city' => $payload->city,
                'state' => $payload->state,
                'zipcode' => $payload->zipCode,
            );

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

            $jobseeker = Worker::where('id', $jobseeker_id)->update($jobseeker);
        }

        if (!$jobseeker) {
            DB::rollBack();
            return response()->json(['error' => 'Server error.']);
        } else {
            DB::commit();
            return $response = [
                'status' => 'success',
                'jobseeker_id' => $jobseeker_id,
                'email_updated' => $is_email_updated,
            ];
        }

    }

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

    public function updateWorkerDetails($payload)
    {

        $jobseeker_id = $this->userData->id;

        if($payload->noResume){
            $rules = array(
                'areas_of_work' => 'required',
                'date_start' => 'required',
                'distance' => 'required',
                'experience' => 'required',
                'expertise' => 'required',
            );
        }else{
            $rules = array(
                'areas_of_work' => 'required',
                'date_start' => 'required',
                'distance' => 'required',
                'experience' => 'required',
                'expertise' => 'required',
                'files_names' => 'required',
            );
        }

        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        // Certificates file name validation
        $with_file = false;
        if($payload->files_names){
            $with_file = true;

            $filesValidator = $this->validateCertificateFiles($payload);
            if($filesValidator){
                return $filesValidator;
            }

            $fileNamesValidator = $this->validateCerficatesName($payload, $jobseeker_id);
            if($fileNamesValidator){
                return $fileNamesValidator;
            }
        }
        
        DB::beginTransaction();
        // saving file

        $experience_id = $payload->experience;
        $expertise_id = $payload->expertise;
        $other_experience = $payload->other_experience;
        $other_expertise = $payload->other_expertise;

        $jobseekerData = array(
            'distance' => $payload->distance,
            'availability_start_date' => Carbon::parse($payload->date_start)->format('Y-m-d'),
        );
        $jobseeker = Worker::where('id', $jobseeker_id)->update($jobseekerData);

        $workAreasTransaction = $this->saveAreasOfWork($payload->areas_of_work, $jobseeker_id, $with_file);
        $workerExperienceData = array(
            'experience_id' => $experience_id,
            'expertise_id' => $expertise_id,
            'experience' => $other_experience,
            'expertise' => $other_expertise,
            'is_worker' => 'false',
        );
        $workerExperienceTransaction = $this->saveWorkerExperience($jobseeker_id, $workerExperienceData);

        if($payload->files_names){
            $fileNames = $payload->files_names;
            $indexCount = 0;
            foreach($fileNames as $key => $name){
                $filePayloadName = 'files.'. $indexCount;
                $resumeExtension = $payload->file($filePayloadName)->extension();
                $resumeFilePathName = $this->createUniqueJobseekerFileName(1, $resumeExtension);

                $this->uploadNewFile($payload, $resumeFilePathName, $filePayloadName, $this->googleBucket);
                $indexCount++;
                $newCertificate = array(
                    'worker_id' => $jobseeker_id,
                    'name' => str_replace ( '.'.$resumeExtension, '', $name), //remove the extension '.pdf'
                    'location' => $resumeFilePathName
                );
                WorkerFile::create($newCertificate);
            }
        }

        if (!$jobseeker || !$workAreasTransaction || !$workerExperienceTransaction) {
            DB::rollBack();
            return response()->json(['error' => 'Server error.']);
        } else {
            DB::commit();
            return $response = [
                'status' => 'success',
                'jobseeker_id' => $jobseeker_id,
            ];
        }
    }

    public function saveAreasOfWork($data, $worker_id, $with_file)
    {   
        //with file - use to detect if with file the array is converted into string because of FORMDATA

        //delete existingg data first
        WorkAreaLists::where('worker_id', $worker_id)->where('worker_type', 'jobseeker')->delete();
        $areas = ($with_file) ? explode(',', $data) : $data;
        $areas_to_num = [];

        //save multiple entry base on the size of areas
        foreach ($areas as $area) {
            WorkAreaLists::insert(['work_area_id' => $area, 'worker_id' => $worker_id, 'worker_type' => 'jobseeker']);
            array_push($areas_to_num, $area);
        }
        return $areas_to_num;
    }

    public function deleteResume($file)
    {
        return Storage::delete($file);
    }

    public function saveWorkerExperience($worker_id, $data)
    {
        return WorkerExperience::updateOrCreate(
            ['worker_id' => $worker_id, 'is_worker' => 'false'],
            $data
        );
    }

    public function sendVerificationToNewEmailAndMobile($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;
        }

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

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

        $emailGenaratedCode = strtoupper($this->generateRandomSixDigitCode());
        $mobileGenaratedCode = strtoupper($this->generateRandomSixDigitCode());
        $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,
        ];

        $emailTranscation = $this->sendEmailVerificationCode($newEmailAddress, $userDataForEmail);
        $sendSMSTransaction = $this->twilio->sendSMS($recevierNumber, $smsMessage);
        $savedCodeTransaction = $this->updateJobseeker($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 sendEmailVerificationCode($email_address, $user_data)
    {
        return ProcessEmailVerificationCode::dispatch($email_address, $user_data)->onConnection('jobs_emails')->onQueue('sendemail');
    }

    public function verificationForNewMobileAndEmailCode($payload)
    {

        $id = Auth::user()->id;
        $jobseeker = Worker::where('id', $id)->first();

        $input_email = strtoupper($payload->input_email);
        $input_mobile = strtoupper($payload->input_mobile);

        if ($input_email != $jobseeker->email_code) {
            return response()->json(['errors' => 'Invalid email verification code']);
        } else if ($input_mobile != $jobseeker->mobile_code) {
            return response()->json(['errors' => 'Invalid mobile verification code']);
        } else if ($input_email == $jobseeker->email_code && $input_mobile == $jobseeker->mobile_code) {
            return response()->json(['success' => 'Verified']);
        }
    }

    public function updateJobseeker($id, $data)
    {
        return Worker::where('id', $id)->update($data);
    }

    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->updateJobseeker($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 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;
        }

        $newEmailAddress = strtolower($payload->email_address);
        $newlyGenaratedCode = strtoupper($this->generateRandomSixDigitCode());

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

        $emailTranscation = $this->sendEmailVerificationCode($newEmailAddress, $userDataForEmail);
        $savedCodeTransaction = $this->updateJobseeker($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 = Worker::where('mobile_code', $inputtedMobileCode)->first();

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

        $emailCodeValidation = Worker::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->updateJobseeker($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 verificationMobileCode($payload)
    {
        $rules = array(
            'mobile_code' => 'required',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        $inputtedCode = $payload->mobile_code;

        $emailCodeValidation = Worker::where('mobile_code', $inputtedCode)->update(['email_verified_at' => now(), 'mobile_code' => null]);

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

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

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

        $inputtedMobileCode = $payload->mobile_code;

        $emailCodeValidation = Worker::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!']);
    }

    public function file_location(){
        switch (now()->format('M')) {
            case 'Jan':
                return '01';
                break;
            case 'Feb':
                return '02';
                break;
            case 'Mar':
               return '03';
                break;
            case 'Apr':
               return '04';
                break;
            case 'May':
                return '05';
                break;
            case 'Jun':
                return '06';
                break;
            case 'Jul':
                return '07';
                break;
            case 'Aug':
                return '08';
                break;
            case 'Sep':
                return '09';
                break;
            case 'Oct':
                return '10';
                break;
            case 'Nov':
                return '11';
                break;
             default:
                return "12";
        }
    }

    public function getFileURL($payload)
    {
        return Storage::get($payload->name);
    }
}
