<?php

namespace App\Modules\Company\Employees;

use App\Modules\Company\Employees\Worker as WorkerModule;
use App\Models\Availabilities;
use App\Models\CompanyBurdenRate;
use App\Models\CompanyExperience;
use App\Models\CompanySubscriptions;
use App\Models\CompanyUser;
use App\Models\WorkAreaLists;
use App\Models\Worker;
use App\Models\Role;
use App\Models\WorkerExperience;
use App\Models\WorkerFile;
use App\Jobs\Company\ProcessSendEmailVerificationForNewWorker;
use Illuminate\Support\Facades\DB;
use App\Traits\CheckEmployeeCountTraits;
use App\Traits\CreateUserActionTraits;
use App\Traits\GenerateAndValidateWorkerID;
use App\Traits\MobileNumberValidatorTraits;
use App\Traits\ZipcodeTraits;
use App\Traits\GoogleBucket;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image;
use App\Events\Company\CreateSearchWorkerEvent;
use App\Modules\RemoteStorage\GoogleBucketGateway;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Log;

class AddWorker extends WorkerModule
{
    use CheckEmployeeCountTraits, GenerateAndValidateWorkerID, MobileNumberValidatorTraits, CreateUserActionTraits, ZipcodeTraits, GoogleBucket;

    public $googleBucket;
    public function __construct(GoogleBucketGateway $googleBucket)
    {
        $this->googleBucket = $googleBucket;
    }

    public function saveData($payload)
    {   

        // Start Transaction
        DB::beginTransaction();

        // worker data
        $companyId = $this->getCurrentUser()->company_id;
        $firstName = ucwords($payload->first_name);
        $lastName = ucwords($payload->last_name);
        $profilePicture = $payload->profile_picture;
        $emailAddress = strtolower($payload->email_address);
        $phoneNum = '+1' . str_replace(array('-', '(', ')', ' '), '', $payload->phone_num);
        $streetAddress = $payload->street_address;
        $city = $payload->city;
        $otherExperience = $payload->other_experience;
        $otherExpertise = $payload->other_expertise;
        $state = $payload->state;
        $zipcode = $payload->zipcode;
        $hourlyRate = $payload->hourly_rate;
        $dateHired = $payload->date_hired;
        $dateHired = date('Y-m-d', strtotime($dateHired));
        $availabilityStatus = 'reserved';
        $additionalDetails = $payload->additional_details;
        $additionalDetails = $additionalDetails == 'null' || $additionalDetails == ''? '' : $additionalDetails;
        $willingDistance = $payload->willing_distance;
        $isActive = 'true';
        $enable2fa = 'false';
        $burdenRateId = null;
        $newExperience = null;
        $newBurden = null;
        $fileNames = $payload->files_names;

        // worker experiences data
        $expertise = $payload->expertise;
        $levelOfExperience = $payload->level_of_experience;
        $isWorker = true;
        $workerType = 'worker';

        // worker areas of work data
        $areasOfWork = is_array($payload->areas_of_work)? $payload->areas_of_work : explode(',', $payload->areas_of_work);
        // role data
        $roles = is_array($payload->roles) ? $payload->roles : explode(',', $payload->roles);

        // check subscription
        $validate = $this->employeeCountValidation($companyId);
        if ($validate) {
            return $validate;
        }

        // validate query params
        $rules = array(
            'first_name' => 'required|max:50',
            'last_name' => 'required|max:50',
            'email_address' => 'required|max:191',
            'phone_num' => 'required|max:50',
            // 'street_address' => 'required|max:50',
            // 'city' => 'required|max:50',
            // 'state' => 'required|max:50',
            'zipcode' => 'required|string|min:5|max:5',
            'hourly_rate' => 'required|numeric|min:1|max:10000',
            'expertise' => 'required',
            'level_of_experience' => 'required',
            'willing_distance' => 'required',
            // 'date_hired' => 'required',
            // 'resume' => 'required|mimes:pdf|max:5120',
            // 'files_names' => 'required',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }
        $validate = $this->validateEmailAddress($emailAddress, $companyId);
        if ($validate) {
            return $validate;
        }
        $validate = $this->validatePhoneNumber($payload->phone_num);
        if ($validate) {
            return $validate;
        }

        $validate = $this->validateZipcodeIfExisting($payload->zipcode);
        if ($validate) {
            return $validate;
        }

         // Certificates file name validation
         $messages = array('files_names.*.max' => 'File name should not exceed 200 characters.', 'files_names.*.min' => 'File name should atleast has 1 character.');
         $rules = array('files_names.*' => 'required|min:1|max:200');
         $validator = Validator::make($payload->all(), $rules, $messages);
 
         if ($validator->fails()){
            if ($validator->fails()){
                return [
                    'errors' => $validator->errors()->first()
                ];
            }
         }

        // generate new worker id
        $workerGeneratedId = $this->generateWorkerId($companyId);

        // validate worker id if already existed on specific company
        $validate = $this->validateWorkerGeneratedId($companyId, $workerGeneratedId);
        if (!$validate) {
            return response()->json(['errors' => ['The worker id has already been taken.']])->setStatusCode(201);
        }

        $hasProfile = $profilePicture == 'null' || $profilePicture == ''?  false : true;
        $profilePicture = $hasProfile? $payload->profile_picture : null;
        // profile picture
        if ($hasProfile) {
           
            $image_64 = $profilePicture;
            $extension = explode('/', explode(':', substr($image_64, 0, strpos($image_64, ';')))[1])[1]; // .jpg .png
            $image = explode(',', $image_64)[1];

            $filePath = $this->createUniqueImageNameWorker($companyId, $extension);
            $this->uploadBase64Image($image, $filePath, $this->googleBucket);
            $profilePicture = $filePath;
        }

        
        // preparing worker data to be saved on database
        $password = Hash::make(Str::random(8));
        $emailVerificationCode = hash('sha256', Str::random(80));
        $workerData = array(
            'worker_id' => $workerGeneratedId,
            'first_name' => $firstName,
            'last_name' => $lastName,
            'email_address' => $emailAddress,
            'password' => $password,
            'email_verification_code' => $emailVerificationCode,
            'is_active' => $isActive,
            'phone_num' => $phoneNum,
            'enable_2fa' => $enable2fa,
            'street_address' => $streetAddress,
            'city' => $city,
            'state' => $state,
            'zipcode' => $zipcode,
            'rate' => $hourlyRate,
            'flat_rate' => $hourlyRate,
            'date_hired' => $dateHired,
            'additional_details' => $additionalDetails,
            'distance' => $willingDistance,
        );

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

        $workerTransaction = $this->saveWorkerData($workerData);
        $workerId = $workerTransaction->id;

        // //process upload resume
        // if($payload->resume){
        //     $resumeExtension = $payload->file('resume')->extension();
        //     $resumeFilePathName = $this->createUniqueResumeNameWorker($companyId, $resumeExtension);
        //     $workerTransaction->resume_file_name = $resumeFilePathName;
        //     $workerTransaction->save(); //save the resume fil path on database
        //     $filePayloadName = 'resume'; //input name of the file on the payload
        //     //upload resume file in the google bucket
        //     $this->uploadNewFile($payload, $resumeFilePathName, $filePayloadName, $this->googleBucket);
        // }
        
        // preparing user data to be saved on database
        $companyUser = array(
            'worker_id' => $workerId,
            'company_id' => $companyId,
            'role_id' => 5, // 5 = worker
            'availability_status' => $availabilityStatus,
        );
        $companyUserAsWorkerTransaction = $this->saveCompanyUser($companyUser);


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

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

      
        //process worker experience
        $this->removeDuplicateBurdenRate($payload->level_of_experience); //to remove duplicate company burden rate on specific company experience bug - 

        //new added experience = new burden rate and new company experience
        if ($levelOfExperience == 0 || $otherExperience) {
            $checkIfNewExpeExisted = $this->getCompanyExperienceAndBurdenData($otherExperience, $companyId); //validate the name first if exisintng

            if($checkIfNewExpeExisted){ //already has the data, get only the commpany_experience_id and burden rate id
                $levelOfExperience = $checkIfNewExpeExisted->company_experience_id;
                $burdenRateId = $checkIfNewExpeExisted->company_burden_rate_id;
                $burdenRate = $checkIfNewExpeExisted->burden_rate;
                $burdenRateType = $checkIfNewExpeExisted->burden_rate_type;

            }else{
                $newExperience = CompanyExperience::create([
                    'company_id' => $companyId,
                    'name' => $otherExperience,
                ]);
    
                $newBurden = CompanyBurdenRate::create([
                    'company_experience_id' => $newExperience->id,
                    'company_id' => $companyId,
                    'burden_rate' => 0.0,
                    'burden_rate_type' => 'percent',
                ]);

                $levelOfExperience = $newExperience->id;
                $burdenRateId = $newBurden->id;
            }
        }else{
            //the selected experience is already existing, get the experience id and its burden rate id
            $checkIfNewExpeExisted = $this->getCompanyExperienceAndBurdenDataUsingId($levelOfExperience, $companyId);
            $levelOfExperience = $checkIfNewExpeExisted->company_experience_id;
            $burdenRateId = $checkIfNewExpeExisted->company_burden_rate_id;
            $burdenRate = $checkIfNewExpeExisted->burden_rate;
            $burdenRateType = $checkIfNewExpeExisted->burden_rate_type;
        }

        $newWorkerExperienceData = array(
            'worker_id' => $workerTransaction->id,
            'experience_id' => $levelOfExperience,
            'expertise_id' => $expertise,
            'is_worker' => 'true',
            'experience' => $payload->other_experience,
            'expertise' => $payload->other_expertise,
            'burden_rate_id' => $burdenRateId
        );
            
        $workerExperienceTransaction = $this->saveWorkerExperience($newWorkerExperienceData);

        
        // preparing worker data additional role to be saved on database
        $companyUserAdditionalRoleTransaction = 1;
        foreach ($roles as $role) {
            if ($role != 5) {
                $companyUserAdditionalRole = array(
                    'worker_id' => $workerId,
                    'company_id' => $companyId,
                    'role_id' => $role,
                    'availability_status' => '',
                );
                $companyUserAdditionalRoleTransaction = $this->saveCompanyUser($companyUserAdditionalRole);
                if (!$companyUserAdditionalRoleTransaction) {
                    DB::rollBack();
                    return response()->json(['error' => 'Server error.']);
                }
            }
        }

        // worker areas
        $workerAreasOfWorkTransaction = '';
        foreach ($areasOfWork as $areaOfWork) {
            $workerAreasOfWorkData = array(
                'work_area_id' => $areaOfWork,
                'worker_id' => $workerId,
                'worker_type' => $workerType,
            );
            $workerAreasOfWorkTransaction = $this->saveWorkerWorkAreas($workerAreasOfWorkData);
            if (!$workerAreasOfWorkTransaction) {
                DB::rollBack();
                return response()->json(['error' => 'Server error.']);
            }
        }



        // preparing worker availability data to be saved on database

        if($burdenRate || $burdenRateType){
            $loadedRate = $burdenRateType == 'percent'? $hourlyRate + (($burdenRate * 0.01) * $hourlyRate): $hourlyRate + $burdenRate;
        }

        $minAvailabilityDate = date("Y-m-d");
        $maxAvailabilityDate = date('Y-m-d', strtotime('+10 years'));
        $workerAvailabilityData = array(
            'worker_id' => $workerId,
            'company_id' => $companyId,
            'worker_type' => 'inhouse',
            'rate' => $loadedRate? $loadedRate: $hourlyRate,
            'min_availability_date' => $minAvailabilityDate,
            'max_availability_date' => $maxAvailabilityDate,
        );
        $companyWorkerAvailabilityTransaction = $this->saveWorkerAvailability($workerAvailabilityData);

        // send email to worker for verifying the email address and setting up the password
        $mobileLink = env('MOBILE_LINK');
        $verificationUrl = $mobileLink . '/setup-account?token=' . $emailVerificationCode;
        $workerData = [
            'first_name' => $firstName,
            'last_name' => $lastName,
            'email_address' => $emailAddress,
            'verification_url' => $verificationUrl,
            'company_name' => $this->getCurrentUserCompanyName()->name,
            'role' => $roles,
        ];
        ProcessSendEmailVerificationForNewWorker::dispatch($emailAddress, $workerData)->onQueue('sendemail')->onConnection('jobs_emails');

         // add user transaction for reports
         $regRoles = $this->regRoles($roles);
         $roleNames = $regRoles->roleNames;
         $lastRole = $regRoles->lastRole; 

        if(count($roles)>1){ 
            $actionTransaction = $this->createUserAction("Added " . $firstName . " " . $lastName . " as ". join(", ", $roleNames). " and " .$lastRole);
        }else{
            $actionTransaction = $this->createUserAction("Added " . $firstName . " " . $lastName . " as ".$lastRole);
        }


        

        // if there's an error or queries don't do their job, rollback!
        if (!$workerTransaction ||
            !$companyUserAsWorkerTransaction ||
            !$companyUserAdditionalRoleTransaction ||
            !$workerExperienceTransaction ||
            !$workerAreasOfWorkTransaction ||
            !$companyWorkerAvailabilityTransaction ||
            !$actionTransaction) {
            DB::rollBack();
            return response()->json(['error' => 'Server error.',
                'workerTransaction' => $workerTransaction,
                'companyUserAsWorkerTransaction' => $companyUserAsWorkerTransaction,
                'companyUserAdditionalRoleTransaction' => $companyUserAdditionalRoleTransaction,
                'workerExperienceTransaction' => $workerExperienceTransaction,
                'workerAreasOfWorkTransaction' => $workerAreasOfWorkTransaction,
                'companyWorkerAvailabilityTransaction' => $companyWorkerAvailabilityTransaction,    
                'actionTransaction' => $actionTransaction,    
            ]);
        } else {
            event(new CreateSearchWorkerEvent($companyUserAsWorkerTransaction)); //Event for creating search data for worker
            DB::commit();
            return response()->json(['success' => 'Data added successfully.'])->setStatusCode(201);
        }
    }

    public function validateEmailAddress($emailAddress, $companyId)
    {
        $companyWorkerData = Worker::select(
            'company_users.company_id')
            ->leftJoin('company_users', 'company_users.worker_id', '=', 'workers.id')
            ->where('workers.email_address', $emailAddress)
            ->withTrashed()
            ->first();
        if ($companyWorkerData) {
            if ($companyId == $companyWorkerData->company_id) {
                return response()->json(['errors' => ["Email address was already been used in your company."]])->setStatusCode(201);
            } else {
                return response()->json(['errors' => ["Email address was already been used by another company."]])->setStatusCode(201);
            }
        }
        return false;
    }

    public function companySubscriptionMaximumEmployee($companyId)
    {
        return CompanySubscriptions::select(
            'company_subscription_rates.max_employee',
            'company_subscription_rates.is_unlimited')
            ->join('company_subscription_rates', 'company_subscriptions.subscription_rate_id', '=', 'company_subscription_rates.id')
            ->where('company_subscriptions.company_id', $companyId)
            ->get()
            ->first();
    }

    public function companyEmployeesCount($company_id)
    {
        $companyWorkerData = CompanyUser::select()
            ->where('company_id', $company_id)
            ->where('role_id', 5)
            ->get();
        return $companyWorkerData->count();
    }

    public function saveWorkerData($workerData)
    {
        return Worker::create($workerData);
    }

    public function saveWorkerExperience($workerExperiencesData)
    {
        return WorkerExperience::create($workerExperiencesData);
    }

    public function saveWorkerWorkAreas($workerAreasOfWorkData)
    {
        return WorkAreaLists::create($workerAreasOfWorkData);
    }

    public function saveWorkerAvailability($workerAvailabilityData)
    {
        return Availabilities::create($workerAvailabilityData);
    }

    public function saveCompanyUser($companyUserData)
    {
        return CompanyUser::create($companyUserData);
    }

    public function getCompanyExperienceData($name, $companyID){
        return CompanyExperience::where('name', $name)->where('company_id', $companyID)->first();
    }

    public function getCompanyExperienceAndBurdenData($name, $companyID){
        if($name != 'Others' || $name != 'Other'){
            return CompanyExperience::select(
                'company_experiences.id as company_experience_id',
                'company_experiences.name',
                'company_burden_rate.company_id',
                'company_burden_rate.id as company_burden_rate_id',
                'company_burden_rate.burden_rate',
                'company_burden_rate.burden_rate_type'
            )
            ->join('company_burden_rate', 'company_burden_rate.company_experience_id', 'company_experiences.id')
            ->where('company_experiences.name', $name)
            ->where('company_experiences.company_id', $companyID)
            ->first();
        }
        return null;
    }

    public function getCompanyExperienceAndBurdenDataUsingId($companyExperienceID, $companyID){
        return CompanyExperience::select(
            'company_experiences.id as company_experience_id',
            'company_experiences.name',
            'company_burden_rate.company_id',
            'company_burden_rate.id as company_burden_rate_id',
            'company_burden_rate.burden_rate',
            'company_burden_rate.burden_rate_type'
        )
        ->join('company_burden_rate', 'company_burden_rate.company_experience_id', 'company_experiences.id')
        ->where('company_experiences.id', $companyExperienceID)
        ->where('company_experiences.company_id', $companyID)
        ->first();
    }

    public function removeDuplicateBurdenRate($companyExperienceID){
        $companyBurdenRate = CompanyBurdenRate::where('company_experience_id', $companyExperienceID)->get();

        if($companyBurdenRate->count() > 1){
            $count = 0;
            foreach($companyBurdenRate as $burdemRate){
                if($count > 0){  //don't remove the first company rate
                    CompanyBurdenRate::where('id', $burdemRate->id)->delete();
                }
                $count++;
            }
        }
    }
    public function regRoles($roles){
        $getRoles = Role::whereIn('id', $roles)->orderBy('id', 'asc')->get();
        $getRoles = json_decode($getRoles);

        $roleNames = array_map(function($obj){
            if($obj->id == 1){
                return "Admin";
            }
            return $obj->name;
        }, $getRoles);

        $vowels = "/[aeiou]/i";
        $roleNames[0] = preg_match($vowels, $roleNames[0][0])? "an ".$roleNames[0]: "a ".$roleNames[0];  
        $lastRole = array_pop($roleNames);

        return (object) array(
            'roleNames'=> $roleNames,
            'lastRole' => $lastRole
        );
    }
}
