<?php

namespace App\Modules\Company\BorrowCart;

use App\Models\CompanyCart;
use App\Models\Search as SearchModel;
use App\Http\Resources\Company\SearchWorker as SearchWorkerResource;
use App\Traits\SearchTraits;
use App\Traits\SortingTraits;
use App\Traits\ZipcodeTraits;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

use App\Modules\Company\BorrowCart\Cart;

class Search extends Cart
{
    use SortingTraits, SearchTraits, ZipcodeTraits;

    protected $zipcodeRadius;
    protected $radiusLimit;

    public function __construct()
    {
        $this->zipcodeRadius = env('APP_STATUS') == 'development'? [2, 5, 10, 15, 20, 25] : [2, 5, 10, 15, 20, 25, 50, 100];
        $this->radiusLimit = env('APP_STATUS') == 'development'? 25 : 100;
    }

    public function searchWorkers($payload)
    {
        
        $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->fetchSearchWorkers($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->fetchSearchWorkers($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 fetchSearchWorkers($payload, $currentZipcodeRadius)
    {
        $companyId = $this->getCurrentUser()->company_id;

        // sorting
        $sortField = $this->sortField($payload, 'worker_company_id'); // 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;
        $state = $payload->state;
        $city = $payload->city;

        

   

        // column searching
        $workerIdSearch = $this->searchField($payload->worker_company_id);
        $zipcodeSearch = $this->searchField($payload->zipcode);
        $experienceSearch = $payload->experience_name;
        $expertiseSearch = (array) $payload->expertise_name;
        $areaOfWorkSearch =  (array)  $payload->work_area_lists;
        $company_type = $payload->company_type == '1' ? 'all' : ($payload->company_type == '2' ? 'affiliated' : 'guest');


        $serachResult = SearchWorkerResource::collection(
            SearchModel::select(
                'search.id',
                'search.worker_id',
                'search.worker_company_id',
                'search.zip_code as zipcode',
                'search.hourly_rate as rate',
                'worker_experiences.experience_id',
                'company_experiences.name as experience_name',
                'worker_experiences.expertise_id',
                'expertises.name as expertise_name',
                'workers.resume_file_name as resume',
            )
                ->join('workers', 'workers.id', '=', 'search.worker_id')
                ->join('company_users', 'company_users.worker_id', '=', 'search.worker_id')
                ->join('companies', 'companies.id', '=', 'company_users.company_id')
                ->join('worker_experiences', 'worker_experiences.worker_id', '=', 'search.worker_id')
                ->join('company_experiences', 'company_experiences.id', '=', 'worker_experiences.experience_id')
                ->join('expertises', 'expertises.id', '=', 'worker_experiences.expertise_id')
                ->with('rating')
                ->with('work_area_lists')->whereHas('work_area_lists', function ($query) use ($areaOfWorkSearch) {
                $query->when(!empty($areaOfWorkSearch), function ($query) use ($areaOfWorkSearch) {
                    return $query->whereIn('work_area_lists.work_area_id', $areaOfWorkSearch);
                });
            })
                ->whereNotNull('workers.email_verified_at')
                ->where('workers.distance', '>=', (int)$zipcodeRangeFormSearch) //show on small miles   
                ->where('company_users.company_id', '!=', $companyId)
                ->where('company_users.role_id', 5) // 5 = worker role id
                ->where('worker_experiences.is_worker', 'true')
                // ->where('company_users.availability_status', 'for-lease')
                ->whereIn('company_users.availability_status', ['for-lease', 'reserved'])
                ->where('companies.is_banned', 'false')
                ->where('companies.enable_borrowing', 'true')
                ->whereIn('search.status', ['Available', 'Requested', 'Haulted'])
                ->whereIn('expertises.id', $expertiseSearch)
                ->when(!empty($workerIdSearch), function ($query) use ($workerIdSearch) {
                    return $query->where('search.worker_company_id', 'LIKE', $workerIdSearch . '%');
                })
                // ->when(!empty($expertiseSearch), function ($query) use ($expertiseSearch) {
                //     return $query->whereIn('expertises.id', $expertiseSearch); 
                // })
                ->when(!empty($experienceSearch), function ($query) use ($experienceSearch) {
                    return $query->whereIn('company_experiences.id', $experienceSearch);
                })
                ->when(!empty($zipcodeSearch), function ($query) use ($zipcodeSearch) {
                    return $query->where('search.zip_code', 'LIKE', $zipcodeSearch . '%');
                })
                ->when($company_type != 'all', function ($query) use ($company_type) {
                    return $query->where('companies.type', $company_type);
                })
                ->whereIn('search.zip_code', $this->getZipcodesByRadius($zipcodeFormSearch, $zipcodeRangeFormSearch))
                ->where('workers.email_verified_at' , '!=', null)
                ->when(!empty($experienceSearch), function ($query) use ($experienceSearch) {
                    return $query->whereIn('company_experiences.id', $experienceSearch);
                })
                // Worker shoud not exist if worker currently on company borrowed cart
                ->whereNotExists(function ($query) use ($companyId) {
                    $query->select(DB::raw(1))
                        ->from('company_carts')
                        ->whereRaw('company_carts.worker_id = search.worker_id')
                        ->whereRaw('company_carts.company_id =' . $companyId)
                        ->whereNull('company_carts.deleted_at');
                })
                ->whereNotExists(function ($query) use ($companyId, $dateFromFormSearch, $dateToFormSearch) {
                    $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 ($companyId, $dateFromFormSearch, $dateToFormSearch) {
                            $query->where('borrow_requests.borrower_company_id', $companyId)
                            ->where('borrow_pending_workers.date_from', $dateFromFormSearch)
                            ->orWhere('borrow_pending_workers.date_to', $dateToFormSearch)
                            ->orWhereBetween('borrow_pending_workers.date_from', [$dateFromFormSearch, $dateToFormSearch])
                            ->orWhereBetween('borrow_pending_workers.date_to', [$dateFromFormSearch, $dateToFormSearch])
                            ->orWhereRaw('? BETWEEN borrow_pending_workers.date_from and borrow_pending_workers.date_to', [$dateFromFormSearch])
                            ->orWhereRaw('? BETWEEN borrow_pending_workers.date_from and borrow_pending_workers.date_to', [$dateToFormSearch]);
                        });
                })
                // Worker shoud not exist if the date is within the date she/he is borrowed
                ->whereNotExists(function ($query) use ($companyId, $dateFromFormSearch, $dateToFormSearch) {
                    $query->select(DB::raw(1))
                        ->from('borrow_approved_histories')
                        ->whereRaw('borrow_approved_histories.worker_id = search.worker_id')
                        ->where(function ($query) use ($companyId, $dateFromFormSearch, $dateToFormSearch) {
                            $query->where('borrow_approved_histories.borrower_company_id', $companyId)
                            ->where('borrow_approved_histories.date_from', $dateFromFormSearch)
                            ->orWhere('borrow_approved_histories.date_to', $dateToFormSearch)
                            ->orWhereBetween('borrow_approved_histories.date_from', [$dateFromFormSearch, $dateToFormSearch])
                            ->orWhereBetween('borrow_approved_histories.date_to', [$dateFromFormSearch, $dateToFormSearch])
                            ->orWhereRaw('? BETWEEN borrow_approved_histories.date_from and borrow_approved_histories.date_to', [$dateFromFormSearch])
                            ->orWhereRaw('? BETWEEN borrow_approved_histories.date_from and borrow_approved_histories.date_to', [$dateToFormSearch]);
                        });
                })
                ->groupBy('search.worker_id')
                ->orderBy($sortField, $sortOrder)
                ->paginate(10)
        )
            ->response()
            ->setStatusCode(201);

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

    }

    public function removeFromCart($payload)
    {

        $id = $payload->id;
        $search_id = $payload->search_id;
        $success = CompanyCart::find($id);
        $success->forceDelete();

        $this->updateStatus($search_id, 'Available');

        return [
            'success' => $success,
            'message' => 'Worker removed from cart successfully!'];
    }

    public function addToCart($payload)
    {
        $company_id = $this->getCurrentUser()->company_id;
        $worker_id = $payload->worker_id;
        $search_id = $payload->search_id;
        $date_from = date('Y-m-d', strtotime($payload->date_from));
        $date_to = date('Y-m-d', strtotime($payload->date_to));
        $return = null;

        // Save data to Company Cart
        $new_item = new CompanyCart;
        $new_item->search_id = $search_id;
        $new_item->company_id = $company_id;
        $new_item->worker_id = $worker_id;
        $new_item->date_from = $date_from;
        $new_item->date_to = $date_to;
        $new_item->created_at = Carbon::now();

        if ($new_item->save()) {
            $updated_item = $this->updateCart($company_id);
            $return = [
                $new_item,
                'success' => true,
                'message' => "Employee " . $payload->worker_company_id . " successfully added to cart from " . date('F d, Y', strtotime($date_from)) . " to " . date('F d, Y', strtotime($date_to)) . ".",
            ];
            $this->updateStatus($search_id, 'Requested');
        } else {
            $return = [
                $new_item,
                'success' => false,
                'message' => 'Error in saving worker to cart.',
            ];
        }
        return $return;
    }

    public function updateStatus($search_id, $status)
    {
        $search_item = SearchModel::where('id', $search_id)->update(['status' => $status]);
        return $search_item;
    }

    public function checkCartExpiration()
    {
        $company_id = $this->getCurrentUser()[0]->company_id;
        $pending_items = CompanyCart::select(
            'company_id',
            'counts(worker_id)',
            'date_updated'
        )
            ->where('company_id', $company_id)
            ->whereNull('deleted_at')
            ->groupBy('companies.id')
            ->get();
        return $pending_items;
    }
    public function updateCart($company_id)
    {
        return CompanyCart::where('company_id', '=', $company_id)
            ->whereNull('deleted_at')
            ->update(['updated_at' => Carbon::now()->format('Y-m-d')]);
    }
}