<?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\CompanyUser;
use App\Models\WorkAreaLists;
use App\Models\Worker;
use App\Models\WorkerFile;
use App\Models\WorkerExperience;
use App\Models\LoanApproved;
use App\Events\Company\WorkerRateUpdatedEvent;
use App\Events\Company\UpdateSearchDataOnReservedWorker;
use App\Traits\ZipcodeTraits;
use App\Traits\CalculateBillableRateTrait;
use App\Traits\CreateUserActionTraits;
use App\Traits\MobileNumberValidatorTraits;
use App\Traits\GoogleBucket;
use Illuminate\Support\Facades\DB;
use App\Modules\RemoteStorage\GoogleBucketGateway;
use Illuminate\Support\Facades\Validator;

class UpdateWorker extends WorkerModule
{
    use CalculateBillableRateTrait, CreateUserActionTraits, MobileNumberValidatorTraits, ZipcodeTraits, GoogleBucket;

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

    public function updateDetails($payload, $id)
    {
        // worker data
        $companyId = $this->getCurrentUser()->company_id;
        $firstName = ucwords($payload->first_name);
        $lastName = ucwords($payload->last_name);
        $emailAddress = strtolower($payload->email_address);
        $phoneNum = '+1' . str_replace(array('-', '(', ')', ' '), '', $payload->phone_num);
        $streetAddress = $payload->street_address;
        $city = $payload->city;
        $state = $payload->state;
        $zipcode = $payload->zipcode;
        $otherExperience = $payload->other_experience;
        $otherExpertise = $payload->other_expertise;
        $hourlyRate = $payload->hourly_rate;
        $dateHired = $payload->date_hired;
        $dateHired = date('Y-m-d', strtotime($dateHired));
        $additionalDetails = $payload->additional_details;
        $additionalDetails = $additionalDetails == 'null' || $additionalDetails == ''? '' : $additionalDetails;

        $willingDistance = $payload->willing_distance;


        // worker experiences data
        $expertise = $payload->expertise;
        $levelOfExperience = $payload->level_of_experience;
        $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);

        // validate query params
        $rules = array(
            'first_name' => 'required|max:50',
            'last_name' => 'required|max:50',
            'email_address' => 'required|unique:workers,email_address,' . $id . '|max:191',
            'phone_num' => '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',

            // 'street_address' => 'required|max:50',
            // 'city' => 'required|max:50',
            // 'state' => 'required|max:50',
            // 'date_hired' => 'required',
        );

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

        //Check if a current worker and has active loan/borrow transaction
        // $hasActiveTransaction = $this->checkActiveBorrowWorkerTransaction($id);
        // if($hasActiveTransaction){
        //     return response()->json(['errors' => ["Unable to proceed on removing worker role. The worker currently has active transactions."]])->setStatusCode(201);
        // }

        // Certificates file name validations
        if($payload->files_names){

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

            $fileNamesValidator = $this->validateCerficatesName($payload, $id);
            if($fileNamesValidator){
                return $fileNamesValidator;
            }
        }

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

        // profile picture
        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->createUniqueImageNameWorker($companyId, $extension);
            $this->uploadBase64Image($image, $filePath, $this->googleBucket);

            $profilePicture = $filePath;
        }

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

        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;
        }

        $workerData = array(
            'first_name' => $firstName,
            'last_name' => $lastName,
            'email_address' => $emailAddress,
            'phone_num' => $phoneNum,
            'street_address' => $streetAddress,
            'city' => $city,
            'state' => $state,
            'zipcode' => $zipcode,
            'flat_rate' => $hourlyRate,
            'date_hired' => $dateHired,
            'additional_details' => $additionalDetails,
            'distance' => $willingDistance,
        );

        if($hasProfile){
            $workerData['profile_picture'] = $profilePicture; //add profile link
        }
        $workerTransaction = $this->updateWorker($id, $workerData);


        // CREATE WORKER FILES
        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->createUniqueFileName(1, $resumeExtension);

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


        // preparing worker availability data to be saved on database
        if($burdenRate || $burdenRateType){
            $loadedRate = $burdenRateType == 'percent'? $hourlyRate + (($burdenRate * 0.01) * $hourlyRate): $hourlyRate + $burdenRate;
        }

        $worker_availability_data = array(
            'rate' => $loadedRate? $loadedRate: $hourlyRate,
        );
        $workerAvailabilityTransaction = $this->updateWorkerAvailabilityRate($id, $worker_availability_data);

        // preparing worker experiences data to be saved on database
        $worker_experiences_data = array(
            'experience_id' => $levelOfExperience != 0 ? $levelOfExperience : $newExperience->id,
            'expertise_id' => $expertise,
            'experience' => $otherExperience,
            'expertise' => $otherExpertise,
            'burden_rate_id' => $burdenRateId,
        );
        $companyWorkerExperienceTransaction = $this->updateWorkerExperience($id, $worker_experiences_data);

        $currentWorkerWorkAreasTransaction = $this->deleteCurrentWorkerAreas($id);
        // worker areas
        foreach ($areasOfWork as $area_of_work) {
            $worker_areas_of_work_data = array(
                'work_area_id' => $area_of_work,
                'worker_id' => $id,
                'worker_type' => $workerType,
            );
            $companyWorkAreaListTransaction = $this->saveWorkerWorkAreas($worker_areas_of_work_data);
        }

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

        $updateCompanyUserWorkerToReserved = $this->updateWorkerAvailabilityToReserved($id, ['availability_status' => 'reserved']);
        event(new UpdateSearchDataOnReservedWorker($id, 'reserved'));
        
        // Add user actions logs
        $userActionTransaction = $this->createUserAction("Updated the data of Employee " . $payload->first_name . " " . $payload->last_name);

        // if there's an error or queries don't do their job, rollback!
        if (!$workerTransaction ||
            !$workerAvailabilityTransaction ||
            !$companyWorkerExperienceTransaction ||
            !$currentWorkerWorkAreasTransaction ||
            !$companyWorkAreaListTransaction ||
            !$deleteCompanyUserRoleExceptWorkerRoleTransaction ||
            !$companyUserAdditionalRoleTransaction ||
            !$updateCompanyUserWorkerToReserved ||
            !$userActionTransaction) {
            DB::rollBack();
            return response()->json(['errors' => 'Server error.']);
        } else {
            DB::commit();
            event(new WorkerRateUpdatedEvent($id, $companyId));
            return response()->json(['success' => 'Data updated successfully.'])->setStatusCode(201);
        }
    }

    public function updateWorker($id, $workerData)
    {
        return tap(Worker::where('id', $id))->update($workerData)->first();
    }

    public function updateWorkerAvailabilityRate($id, $worker_availability_data)
    {
        return Availabilities::where('worker_id', $id)
            ->where('worker_type', 'inhouse')
            ->update($worker_availability_data);
    }

    public function updateWorkerAvailabilityToReserved($id, $data){
        return CompanyUser::where('worker_id', $id)->update($data);
    }

    public function updateWorkerExperience($id, $worker_experiences_data)
    {
        return WorkerExperience::where('worker_id', $id)->update($worker_experiences_data);
    }

    public function deleteCurrentWorkerAreas($id)
    {
        return WorkAreaLists::where('worker_id', $id)
            ->where('work_area_lists.worker_type', 'worker')
            ->delete();
    }

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

    public function deleteCompanyUserRoleExceptWorkerRole($id)
    {
        CompanyUser::where('worker_id', $id)
            ->whereNotIn('company_users.role_id', [5]) // 5 = worker
            ->delete();
        return true;
    }

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

    public function updateAvailabilityStatus($payload, $id)
    {
        // start transaction
        DB::beginTransaction();

        $availability_status = $payload->availability_status;

        // validate query params
        $rules = array(
            'availability_status' => 'required|in:reserved,for-lease,leased',
        );
        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        $form_data = array(
            'availability_status' => $availability_status,
        );
        $companyUserTransaction = CompanyUser::where('worker_id', $id)
            ->where('role_id', 5) // 5 = worker
            ->update($form_data);
        
        $workerName = $this->getWorkerName($id);

        $userActionTransaction = $this->createUserAction("Changed the employee availability status of " . $workerName . " into " . $payload->availability_status);
        event(new UpdateSearchDataOnReservedWorker($id, $payload->availability_status));

        // if there's an error or queries don't do their job, rollback!
        if (!$companyUserTransaction ||
            !$userActionTransaction) {
            DB::rollBack();
            return response()->json(['error' => 'Server error.']);
        } else {
            DB::commit();
            return response()->json(['success' => 'Data updated successfully.'])->setStatusCode(201);
        }
    }

    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 checkActiveBorrowWorkerTransaction($workerID){
        return LoanApproved::select(
            'loan_approved_histories.loaner_company_id',
            'loan_approved_histories.borrower_company_id',
            'loan_approved_histories.date_from',
            'loan_approved_histories.date_to',
            'loan_approved_histories.worker_id',
        )
        ->whereRaw('loan_approved_histories.date_to >= CURDATE()')
        ->where('loan_approved_histories.worker_id', $workerID)
        ->distinct()
        ->first();
    }
    public function getWorkerName($id){
        $workerName = Worker::select(
            'workers.first_name',
            'workers.last_name'
            )
            ->where('id', $id)
            ->first();
        return $workerName->first_name." ".$workerName->last_name;
    }
}
