<?php

namespace App\Modules\Company\WorkerRequest;
use App\Models\WorkerRequest;
use App\Http\Resources\Company\Notification\WorkerRequestResource;
use App\Traits\SearchTraits;
use App\Traits\SortingTraits;
use App\Traits\ZipcodeTraits;
use App\Traits\CreateUserActionTraits;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use App\Traits\UserTraits;
use App\Models\Search;
use App\Models\WorkerRequestApplication;
use App\Models\State;
use App\Models\City;
use App\Models\ZipcodeRange;
use App\Models\Company;
use App\Models\CompanySettings;
use App\Models\Notifications;
use App\Events\Company\PusherNotificationEvent as CompanyNotificationEvent;
use Illuminate\Support\Facades\Log;





class Find{
    use SortingTraits, ZipcodeTraits, UserTraits, SearchTraits, CreateUserActionTraits;

    public $companyId;
    public $subscribedZipcode;
    public $radiusLimit;
    public $zipcodeRadius;
    public $companySettings;

    public function setZipcodeRadius()
    {
        $this->companyId = $this->getCurrentUser()->company_id;
        // $this->zipcodeRadius = [2, 5, 10, 15, 20, 25, 50, 100];
        
        $this->companySettings = $this->getCompanySubscribedZipcode($this->companyId);
        $this->subscribedZipcode = $this->companySettings->zipcode;   //subscribed zipcode or company zipcode
        $this->radiusLimit = $this->companySettings->radius;     //subscribed radius or default radius 2 miles

        $this->zipcodeRadius = array_filter([2, 5, 10, 15, 20, 25, 50, 100], function($miles){
                return $miles <= $this->radiusLimit;
            }, 
        );
    }

    public function fetchWorkerRequests($payload)
    {
        $this->setZipcodeRadius();
        
        
        $zipcodeRadius = $this->zipcodeRadius;  

        $currentRadius = $payload->currentRadius;
        if($currentRadius){
            $currentRadiusKey = array_keys($zipcodeRadius, $currentRadius)[0]; // get the array key of the current radius
        }

        $goToNextRange = $payload->goToNextRange  == "true" ? true : false;
        if(!$currentRadius){ //start from 0
            $currentRadiusKey = 0;
            $currentRadius = $zipcodeRadius[$currentRadiusKey]; //2 Miles
            return $this->handleSearch($payload, $currentRadius, null);

        }else if($currentRadius >= 2){
            if($goToNextRange){
                $payload->page = "1";
                return $this->handleSearch($payload, $currentRadius, $currentRadiusKey + 1);  //proceed to next radius on page 1
            }else{
                return $this->fetchListOfWorkerRequests($payload, $currentRadius); //prev and next of the same radius
            }
        }
    }

    public function handleSearch($payload, $currentRadius, $currentRadiusKey){
        $zipcodeRadius = $this->zipcodeRadius;
        $startFromThisRange = $zipcodeRadius; 
        if($currentRadiusKey){ //if null start from first 
            $startFromThisRange =  array_slice($zipcodeRadius, $currentRadiusKey); //array splice remove data from array, starting from key 0 - length
        }

        foreach($startFromThisRange as $key => $currentRadius){
            $searchResult = $this->fetchListOfWorkerRequests($payload, $currentRadius);
            $queryResult = $searchResult->getData(); //make json object manipulable 
            // if(count($queryResult->data) > 0 || $currentRadius == 100){ //check if there is a result in data query
            if(count($queryResult->data) > 0 || $currentRadius == $this->radiusLimit){ //check if there is a result in data query
                return $searchResult;
            }
        }
    }
    public function fetchListOfWorkerRequests($payload, $currentZipcodeRadius)
    {
       
        $companyId = $this->getCurrentUser()->company_id;

        // sorting
        $sortField = $this->sortField($payload, 'date_end'); // worker_company_id = default column to search
        $sortOrder = $this->sortOrder($payload, 'asc');

        // form searching
        $dateFromFormSearch = date('Y-m-d', strtotime($payload->date_from));
        $dateToFormSearch = date('Y-m-d', strtotime($payload->date_to));
        $zipcodeFormSearch = $payload->zip_code;
        $zipcodeRangeFormSearch = $currentZipcodeRadius;

        // get list of zipcodes by state and city
        // $state = $payload->state;
        // $city = $payload->city;
        // $zipcode = $this->getZipcodeByStateCity($state, $city)->zipcode;
        $subscribedZipcode = $this->getCompanySubscribedZipcode($companyId)->zipcode;
        
        $listOfZipcode = $this->getZipcodesByRadius($subscribedZipcode, $zipcodeRangeFormSearch); // get list of zipcodes by range
        $listOfZipcodeID = $this->getListOfZipcodeID($listOfZipcode); // get the id of list of zipcodes 
        
        $experienceSearch = $payload->experience_name;
        $expertiseSearch = (array) $payload->expertise;
        $areaOfWorkSearch =  (array)  $payload->work_area_lists;


        $findResult = WorkerRequestResource::collection(
            WorkerRequest::select(
                'worker_requests.id',
                'worker_requests.company_id',
                'worker_requests.expertise_id',
                'worker_requests.experience',
                'worker_requests.description',
                'worker_requests.status',
                'worker_requests.zipcode_range_id',
                'worker_requests.date_start',
                'worker_requests.date_end',
                'states.name as state',
                'states.city as city',
                'expertises.name as expertise_name'
                )
                ->join('expertises', 'expertises.id', '=', 'worker_requests.expertise_id')
                ->join('zipcoderange', 'worker_requests.zipcode_range_id', '=', 'zipcoderange.id')
                ->join('states', 'zipcoderange.state_id', '=', 'states.id')
                ->join('companies', 'companies.id', '=', 'worker_requests.company_id')
                ->with('worker_request_work_areas')->whereHas('worker_request_work_areas', function ($query) use ($areaOfWorkSearch) {
                    $query->when(!empty($areaOfWorkSearch), function ($query) use ($areaOfWorkSearch) {
                        return $query->whereIn('worker_request_work_areas.work_area_id', $areaOfWorkSearch);
                    });
                })
                ->whereBetween('date_end', [$dateFromFormSearch, $dateToFormSearch])
                ->where('worker_requests.company_id', '!=', $companyId)
                ->where('companies.is_banned', 'false')
                ->where('companies.enable_borrowing', 'true')
                ->whereIn('worker_requests.status', ['active', 'completed'])
                ->when(!empty($expertiseSearch), function ($query) use ($expertiseSearch){
                    return $query->whereIn('expertise_id', $expertiseSearch);
                })
                ->when(!empty($listOfZipcodeID), function ($query) use ($listOfZipcodeID){
                    return $query->whereIn('worker_requests.zipcode_range_id', $listOfZipcodeID);
                })
                ->groupBy('worker_requests.id')
                ->orderBy($sortField, $sortOrder)
                ->paginate(10)
        )
            ->response()
            ->setStatusCode(201);

        $queryResult = $findResult->getData(); //make json object manipulatable 
        $queryResult->meta->currentRadius = $currentZipcodeRadius;
        $queryResult->meta->radiusLimit = $this->radiusLimit;
        return response()->json($queryResult)->setStatusCode(201);

    }

    public function getWorkerRequest($payload){

        $workerRequestId = $payload->id;

        $companyId = $this->getCurrentUser()->company_id;

        $findResult = WorkerRequestResource::collection(
            WorkerRequest::select(
                'worker_requests.id',
                'worker_requests.company_id',
                'worker_requests.expertise_id',
                'worker_requests.experience',
                'worker_requests.description',
                'worker_requests.status',
                'worker_requests.zipcode_range_id',
                'worker_requests.date_start',
                'worker_requests.date_end',
                'states.name as state',
                'states.city as city',
                'expertises.name as expertise_name'
                )
                ->join('expertises', 'expertises.id', '=', 'worker_requests.expertise_id')
                ->join('zipcoderange', 'worker_requests.zipcode_range_id', '=', 'zipcoderange.id')
                ->join('states', 'zipcoderange.state_id', '=', 'states.id')
                ->join('companies', 'companies.id', '=', 'worker_requests.company_id')
                ->with('worker_request_work_areas')
                ->where('worker_requests.id', $workerRequestId)
                ->where('worker_requests.company_id', '!=', $companyId)
                ->where('companies.is_banned', 'false')
                ->where('companies.enable_borrowing', 'true')
                ->where('worker_requests.status', 'active')
                ->groupBy('worker_requests.id')
                ->paginate(10)
        )
            ->response()
            ->setStatusCode(201);

        $queryResult = $findResult->getData(); //make json object manipulatable 
        // $queryResult->meta->currentRadius = $currentZipcodeRadius;
        return response()->json($queryResult)->setStatusCode(201);

    }

    
    public function fetchWorkers($payload)
    {   
        $currentUserId = $this->getCurrentUser()->company_id;

        $workerRequestId = $payload->workerRequestId;
        $companyRequestId = $payload->companyId;
        $expertiseId =  $payload->expertise_id;
        $areasOfWork =  $payload->work_area_lists;
        $experience = $payload->experience;
        $dateStart = $payload->date_start;
        $dateEnd = $payload->date_end;
        $state = $payload->state;
        $city = $payload->city;
        $zipcodeRange = $payload->zipcode_range;
        
        $zipcode = $this->getZipcodeByStateCity($state, $city)->zipcode;
   
        if(!$zipcodeRange){
            $zipcodeRange = $this->getCurrentRadius($currentUserId, $zipcode);
        }
    
        $sortField = $this->sortField($payload, 'full_name'); 
        $sortOrder = $this->sortOrder($payload, 'asc');
        $searchName = $this->searchField($payload->full_name);

        //column search
        $workerIdSearch = $this->searchField($payload->worker_company_id);

        $workerResult = Search::select(
            'search.id',
            'search.worker_id',
            'search.worker_company_id',
            'search.zip_code as zipcode',
            'search.original_rate as rate',
            'company_users.company_id',
            'company_experiences.name as experience_name',
            'worker_experiences.experience_id',
            'worker_experiences.expertise_id', 'expertises.name as expertise_name',
            DB::raw('CONCAT(workers.first_name, " ", workers.last_name) as full_name'))
            ->join('workers', 'workers.id', '=', 'search.worker_id')
            ->join('company_users', 'search.worker_id', '=', 'company_users.worker_id')
            ->join('worker_experiences', 'worker_experiences.worker_id', '=', 'company_users.worker_id')
            ->join('company_experiences', 'company_experiences.id', '=', 'worker_experiences.experience_id')
            ->join('expertises', 'expertises.id', '=', 'worker_experiences.expertise_id')
            ->with('work_area_lists')->whereHas('work_area_lists', function ($query) use ($areasOfWork) {
                return $query->whereIn('work_area_lists.work_area_id', $areasOfWork);
            })
            ->when(!empty($workerIdSearch), function ($query) use ($workerIdSearch) {
                return $query->where('search.worker_company_id', 'LIKE', $workerIdSearch . '%');
            })
            ->when(!empty($searchName), function ($query) use ($searchName) {
                return $query->where(function ($query) use ($searchName) {
                    $query->where('workers.first_name', 'LIKE',$searchName . '%')
                        ->orWhere('workers.last_name', 'LIKE', $searchName . '%')
                        ->orWhere(DB::raw('CONCAT(workers.first_name, " ", workers.last_name)'), 'LIKE', $searchName . '%');
                });
            })
            ->with('rating')
            ->whereNotNull('workers.email_verified_at')
            ->where('workers.distance', '>=', (int)$zipcodeRange)
            ->whereIn('search.status', ['Available', 'Requested', 'Haulted'])
            // ->where('company_users.availability_status', 'reserved')
            ->whereIn('company_users.availability_status', ['for-lease', 'reserved'])
            ->where('company_experiences.name', $experience)
            ->where('worker_experiences.expertise_id', $expertiseId)
            ->where('company_users.company_id', $currentUserId)
            ->where('company_users.role_id', 5)

            ->whereNotExists(function ($query) use ($companyRequestId) {
                $query->select(DB::raw(1))
                    ->from('company_carts')
                    ->whereRaw('company_carts.worker_id = search.worker_id')
                    ->whereRaw('company_carts.company_id =' . $companyRequestId)
                    ->whereNull('company_carts.deleted_at');
            })
            ->whereNotExists(function ($query) use ($workerRequestId) {
                $query->select(DB::raw(1))
                    ->from('worker_request_applications')
                    ->whereRaw('search.worker_id = worker_request_applications.worker_id')
                    ->whereRaw('worker_request_applications.worker_request_id =' . $workerRequestId);
                    
            })
            ->whereNotExists(function ($query) use ($companyRequestId, $dateStart, $dateEnd) {
                $query->select(DB::raw(1))
                    ->from('borrow_pending_workers')
                    ->join('borrow_requests', 'borrow_requests.id', '=', 'borrow_pending_workers.borrow_request_id')
                    ->whereRaw('borrow_pending_workers.worker_id = search.worker_id')
                    ->whereIn('borrow_pending_workers.status', ['Approved'])
                    ->where(function ($query) use ($companyRequestId, $dateStart, $dateEnd) {
                        $query->where('borrow_requests.borrower_company_id', $companyRequestId)
                        ->where('borrow_pending_workers.date_from', $dateStart)
                        ->orWhere('borrow_pending_workers.date_to', $dateEnd)
                        ->orWhereBetween('borrow_pending_workers.date_from', [$dateStart, $dateEnd])
                        ->orWhereBetween('borrow_pending_workers.date_to', [$dateStart, $dateEnd])
                        ->orWhereRaw('? BETWEEN borrow_pending_workers.date_from and borrow_pending_workers.date_to', [$dateStart])
                        ->orWhereRaw('? BETWEEN borrow_pending_workers.date_from and borrow_pending_workers.date_to', [$dateEnd]);
                    });
            })
            // Worker shoud not exist if the date is within the date she/he is borrowed
            ->whereNotExists(function ($query) use ($companyRequestId, $dateStart, $dateEnd) {
                $query->select(DB::raw(1))
                    ->from('borrow_approved_histories')
                    ->whereRaw('borrow_approved_histories.worker_id = search.worker_id')
                    ->where(function ($query) use ($companyRequestId, $dateStart, $dateEnd) {
                        $query->where('borrow_approved_histories.borrower_company_id', $companyRequestId)
                        ->where('borrow_approved_histories.date_from', $dateStart)
                        ->orWhere('borrow_approved_histories.date_to', $dateEnd)
                        ->orWhereBetween('borrow_approved_histories.date_from', [$dateStart, $dateEnd])
                        ->orWhereBetween('borrow_approved_histories.date_to', [$dateStart, $dateEnd])
                        ->orWhereRaw('? BETWEEN borrow_approved_histories.date_from and borrow_approved_histories.date_to', [$dateStart])
                        ->orWhereRaw('? BETWEEN borrow_approved_histories.date_from and borrow_approved_histories.date_to', [$dateEnd]);
                    });
            })
            ->distinct()
            ->groupBy('search.worker_id')
            ->orderBy($sortField, $sortOrder)
            ->get();

        return response()->json($workerResult)->setStatusCode(201);

    }

    public function applyWorkers($payload){
        $workerRequestId = $payload->workerRequestId;
        $selectedPendingWorkers = $payload->selectedPendingWorkers;

        $requestData = (object) array(
            'id' => $workerRequestId,
        );

        $borrowerCompanyID = WorkerRequest::select('company_id')
                                ->where('id', $workerRequestId)
                                ->first()
                                ->company_id;

        foreach($selectedPendingWorkers as $key=>$value){
            $appliedWorker = WorkerRequestApplication::create([
                'worker_request_id' => $workerRequestId,
                'company_id' => $value['company_id'],
                'worker_id' => $value['worker_id'],
                ]
            );

            if($appliedWorker){
                $companyName = $this->getCompanyName($borrowerCompanyID); 
                $this->createUserAction("Employee ".$value['worker_company_id']." has been applied to employee request of company ".$companyName);

                $message = "A company applied its employee to your request.";

                $notificationData = array(
                    'type' => 'Borrow Request',
                    'company_id' => $borrowerCompanyID,
                    'message' => $message,
                    'link' => '/company/worker-request/manage/'.$workerRequestId.'/applicant',
                    "is_read" => false,
                );
                Notifications::create($notificationData);
                event(new CompanyNotificationEvent($notificationData));
            }

        }

       
        return response()->json(['data'=>'Sucessfully Applied Workers'])->setStatusCode(201);
    }
    
    public function getZipcodeByStateCity($state, $city){
        
        $state = State::where('name', $state)
            ->where('city', $city)
            ->first();
        

        $zipcodeRangeData = ZipcodeRange::select(
            'id',
            'zipcode', 
        )->where('state_id', $state->id)->first();

        return $zipcodeRangeData;
    }

    public function getListOfZipcodeByRadius($subscribedZipcode, $subscribedRadius){
        $zipcodeRadius = [2, 5, 10, 15, 20, 25, 50, 100];
        $listOfZipcode = [];   //list of zipcode within the zipcode range

        // Get all the zipcodes within the zipcode range
        foreach($zipcodeRadius as $radius){
            if($radius <= $subscribedRadius ){
                $zipcodeListByRadius = $this->getZipcodesByRadius($subscribedZipcode, $radius);
                $listOfZipcode = [...$listOfZipcode, ...$zipcodeListByRadius];
            }else{
                break;
            }
        }
        return $listOfZipcode;
    }

    public function getListOfZipcodeID($listOfZipcode){
        $data = ZipcodeRange::select('id')->whereIn('zipcode', $listOfZipcode);
        return $data;
    }

    public function getCompanyName($companyId){
        return Company::select('name')->where('id',$companyId)->first()->name;
    }

    public function getCompanySubscribedZipcode($companyID){
        //Getting zip code
        $isSubscribedZipcode = CompanySettings::where('company_id', $companyID)
        ->where('key', 'subscribed_zipcode')
        ->first();

        //Getting radius
        $isSubscribedRadius = CompanySettings::where('company_id', $companyID)
        ->where('key', 'subscribed_zipcode_radius')
        ->first();

        if($isSubscribedZipcode && $isSubscribedRadius){
            return (object) array(
                "zipcode" => $isSubscribedZipcode->value, 
                "radius" => $isSubscribedRadius->value
            );
        }
        $companyZipcode = Company::select('zipcode')->where('id', $companyID)->first()->zipcode;
        return (object) array(
            "zipcode" => $companyZipcode, 
            "radius" => 2
        );
        // return $this->getZipcodeID($this->userData->zipcode);
    }

    public function getCurrentRadius($currentUserId, $zipcode){
        
        $settings = $this->getCompanySubscribedZipcode($currentUserId);
        $zipcodeRadius = [2, 5, 10, 15, 20, 25, 50, 100];

        foreach($zipcodeRadius as $radius){
            if($radius > $settings->radius){
                break;
            }
            
            $listOfZipcodes = $this->getZipcodesByRadius($settings->zipcode, $radius);
            
            if(in_array($zipcode, $listOfZipcodes) || $zipcode == $settings->zipcode){
                return $radius;
            }
        } 
    }
}