<?php

namespace App\Modules\Company\WorkerRequest;

use App\Http\Resources\Company\WorkerRequest\WorkerRequestApplicationManagementCollection; 
use App\Http\Resources\Company\WorkerRequest\WorkerRequestApplicationManagementResource;
use App\Models\BorrowPendingWorkers;
use App\Models\CompanyCart;
use App\Models\Notifications; 
use App\Models\Search;
use App\Models\WorkerRequestApplication;
use App\Traits\CustomPaginationTrait;
use App\Traits\CreateUserActionTraits;

use Carbon\Carbon;

class WorkerRequestApplicationManagementModule
{
    /** Traits */
    use CustomPaginationTrait; 
    use CreateUserActionTraits; 

    /**
     * Fetch Items. 
     * 
     * @params array $data 
     * @params int   $workerRequestId 
     * 
     * @return WorkerRequestApplicationManagementCollection
     */
    public function fetchItems (array $data, int $workerRequestId) : WorkerRequestApplicationManagementCollection
    {
        $items      =       WorkerRequestApplication::query()
                                ->with('company', 'worker', 'worker.experience', 'worker.expertise', 'worker.workAreas')
                                ->where('worker_request_id', $workerRequestId)
                                ->when(
                                    isset($data['status']),
                                    function ($query) use ($data)
                                    {
                                        $status = $data['status']; 

                                        $query->where('status', '=', $status);
                                    }
                                )
                                ->get();

        //! Hacky way of sorting 
        //! Reference : https://laracasts.com/discuss/channels/laravel/remove-keys-from-collection
        if ( isset($data['sortBy']) && isset($data['sortOrder']) )
        {
            $field      =   $data['sortBy']; 
            $direction  =   $data['sortOrder'] == 'ascend' ? 'asc' : 'desc'; 

            $items = $items->sortBy( 
                callback : function ($query) use ($field)
                {
                    switch($field)
                    {
                        case "applicationWorkerEmployeeId": 
                            return $query->worker->worker_id; 
                            break; 

                        case "applicationCompanyName": 
                            return $query->company->name;
                            break; 

                        case "applicationWorkerExpertise": 
                            return $query->worker->expertise->name;
                            break; 

                        case "applicationWorkerExperience":       
                            return $query->worker->experience->name; 
                            break; 

                        case "applicationWorkerHourlyRate": 
                            return $query->worker->rate; 
                            break; 

                        case "applicationWorkerZipcode": 
                            return $query->worker->zipcode; 
                            break; 

                        case "applicationStatus": 
                            return $query->status; 
                            break; 
                        }
                },
                descending      :  $direction == 'desc' ? true : false 
            ); 
        }

        $items = $this->customPaginate(
            items      : $items,
            currentPage: $data['currentPage'] ?? 1,
            sizePerPage: $data['sizePerPage'] ?? 20
        );

        return new WorkerRequestApplicationManagementCollection($items);
    }

    /**
     * Fetch Item. 
     * 
     * @params int   $workerRequestId
     * @params int   $applicationId 
     * 
     * @return WorkerRequestApplicationManagementResource
     */
    public function fetchItem (int $workerRequestId, int $applicationId) : WorkerRequestApplicationManagementResource
    {
        $item           =   WorkerRequestApplication::query()
                                ->with('company', 'worker')
                                ->where(
                                    [
                                        [ 'id',                '=', $applicationId ], 
                                        [ 'worker_request_id', '=', $workerRequestId]  
                                    ]
                                )
                                ->firstOrFail();

        return WorkerRequestApplicationManagementResource::make($item);
    } 

    /**
     * Accept Item. 
     * 
     * @params int   $workerRequestId
     * @params int   $applicationId 
     * 
     * @return void
     */
    public function acceptItem (int $workerRequestId, int $applicationId) : void
    {
        $item           =   WorkerRequestApplication::query()
                                ->where(
                                    [
                                        [ 'id',                '=', $applicationId ], 
                                        [ 'worker_request_id', '=', $workerRequestId]  
                                    ]
                                )
                                ->firstOrFail();

        $item->status    =   'accepted'; 
        $item->save(); 
    } 

    /**
     * Decline Item. 
     * 
     * @params int   $workerRequestId
     * @params int   $applicationId 
     * 
     * @return void
     */
    public function declineItem (int $workerRequestId, int $applicationId) : void
    {
        $item           =   WorkerRequestApplication::query()
                                ->where(
                                    [
                                        [ 'id',                '=', $applicationId ], 
                                        [ 'worker_request_id', '=', $workerRequestId]  
                                    ]
                                )
                                ->firstOrFail();

        $item->status    =   'declined'; 
        $item->save();

        $this->createUserAction('Declined an employee request application');

        $notificationData = array(
            'type'       => 'Employee Request',
            'company_id' => $item->company_id,
            'message'    => "An Employee that you have applied in a Employee Request has been declined.",
            'link'       => '/company/worker-request/find',
            "is_read"    => false,
        );

        Notifications::create($notificationData);
    }

    /**
     * Add Item to Cart. 
     * 
     * @params int   $workerRequestId
     * @params int   $applicationId 
     * 
     * @return bool
     */
    public function cartItem(int $workerRequestId, int $applicationId): bool
    {
        $item           =   WorkerRequestApplication::query()
                                ->with('workerRequest')
                                ->where(
                                    [
                                        ['id',                '=', $applicationId],
                                        ['worker_request_id', '=', $workerRequestId]
                                    ]
                                )
                                ->firstOrFail();

        $searchId       =   Search::query()
                                ->select('id')
                                ->where('worker_id', '=', $item->worker_id)
                                ->first()
                                ->id;

        //! IF DATE FROM EXCEEDS CURRENT DATE, ADJUST TODAY 
        $carbonDateFrom  = Carbon::createFromDate($item->workerRequest->date_start);

        $dateFrom = $carbonDateFrom->gt(Carbon::now()) == true ? $item->workerRequest->date_start : Carbon::now();

        //! Check If Worker is already Reserved In Cart
        $cartCount          =   CompanyCart::query()
                                ->select('id')
                                ->where(
                                    [ 
                                        ['search_id',  '=', $searchId       ],
                                        ['worker_id',  '=', $item->worker_id]
                                    ]
                                )
                                ->get()
                                ->count(); 

         //! Check If Worker is already Reserved In Borrow Request
        $borrowPendingCount = BorrowPendingWorkers::query()
                                    ->where('worker_id', '=', $item->worker_id)
                                    ->whereIn('status', ['Pending', 'Approved'])
                                    ->whereDate('date_to', '>=', $dateFrom)
                                    ->get()
                                    ->count();

        if ($cartCount > 0 || $borrowPendingCount > 0)
        {
            return false; 
        } 
        
        CompanyCart::create(
            [
                'search_id'  => $searchId,
                'company_id' => $item->workerRequest->company_id,
                'worker_id'  => $item->worker_id,
                'date_from'  => $dateFrom,
                'date_to'    => $item->workerRequest->date_end
            ]
        );
        
        $item->status    =   'in-cart';
        $item->save();

        return true;
    }

    /**
     * Remove Item From Cart. 
     * 
     * @params int   $workerRequestId
     * @params int   $applicationId 
     * 
     * @return void
     */
    public function unCartItem(int $workerRequestId, int $applicationId): void
    {
        $item           =   WorkerRequestApplication::query()
                                ->with('workerRequest')
                                ->where(
                                    [
                                        ['id',                '=', $applicationId],
                                        ['worker_request_id', '=', $workerRequestId]
                                    ]
                                )
                                ->firstOrFail();

        CompanyCart::query()
            ->select('id')
            ->where(
                [
                    ['company_id', '=', $item->workerRequest->company_id],
                    ['worker_id',  '=', $item->worker_id]
                ]
            )
            ->first()
            ->forceDelete();

        $item->status    =   'pending';
        $item->save();
    } 

    /**
     * Batch Accept Items. 
     * 
     * @params array $data
     * @params int   $applicationId 
     * 
     * @return void
     */
    public function batchItemsAccept (array $data, int $workerRequestId) : void
    {
        WorkerRequestApplication::query()
            ->select('id')
            ->where('worker_request_id', '=', $workerRequestId)
            ->whereIn('id', $data['applicationIds'])
            ->update(
                [
                    'status'    =>  'accepted'
                ]
            );
    } 

    /**
     * Batch Accept Items. 
     * 
     * @params array $data
     * @params int   $applicationId 
     * 
     * @return void
     */
    public function batchItemsDecline (array $data, int $workerRequestId) : void
    {
        WorkerRequestApplication::query()
            ->select('id')
            ->where('worker_request_id', '=', $workerRequestId)
            ->whereIn('id', $data['applicationIds'])
            ->update(
                [
                    'status'    =>  'declined'
                ]
            );

        $actionMessage = count($data['applicationIds']) > 1 ? "Declined multiple employee request applications" : "Declined an employee request application";

        $this->createUserAction($actionMessage);

        $items  =   WorkerRequestApplication::query()
                        ->select('id', 'company_id')
                        ->where('worker_request_id', '=', $workerRequestId)
                        ->whereIn('id', $data['applicationIds'])
                        ->get(); 


        foreach($items as $item)
        {
            $notificationData = array(
                'type'       => 'Employee Request',
                'company_id' => $item->company_id,
                'message'    => "An Employee that you have applied in a Employee Request has been declined.",
                'link'       => '/company/worker-request/find',
                "is_read"    => false,
            );
    
            Notifications::create($notificationData);
        }
    }

    /**
     * Batch Cart Items. 
     * 
     * @params array $data
     * @params int   $applicationId 
     * 
     * @return array
     */
    public function batchItemsCart(array $data, int $workerRequestId): array
    {
        $items  =   WorkerRequestApplication::query()
                        ->with('workerRequest')
                        ->where('worker_request_id', '=', $workerRequestId)
                        ->whereIn('id', $data['applicationIds'])
                        ->get();

        $itemsTotal     =   $items->count(); 
        $itemsAdded     =   0; 

        foreach ($items as $item)
        {
            $searchId       =   Search::query()
                                    ->select('id')
                                    ->where('worker_id', '=', $item->worker_id)
                                    ->first()
                                    ->id;

            //! IF DATE FROM EXCEEDS CURRENT DATE, ADJUST TODAY 
            $carbonDateFrom  = Carbon::createFromDate($item->workerRequest->date_start);

            $dateFrom = $carbonDateFrom->gt(Carbon::now()) ? $item->workerRequest->date_start : Carbon::now();

            //! Check If Worker is already Reserved In Cart
            $cartCount          =   CompanyCart::query()
                                    ->select('id')
                                    ->where(
                                        [ 
                                            ['search_id',  '=', $searchId       ],
                                            ['worker_id',  '=', $item->worker_id]
                                        ]
                                    )
                                    ->get()
                                    ->count(); 

            //! Check If Worker is already Reserved In Borrow Request
            $borrowPendingCount = BorrowPendingWorkers::query()
                                        ->where('worker_id', '=', $item->worker_id)
                                        ->whereIn('status', ['Pending', 'Approved'])
                                        ->whereDate('date_to', '>=', $dateFrom)
                                        ->get()
                                        ->count();

            if ($cartCount > 0 || $borrowPendingCount > 0)
            {
                continue;
            }

            CompanyCart::create(
                [
                    'search_id'  => $searchId,
                    'company_id' => $item->workerRequest->company_id,
                    'worker_id'  => $item->worker_id,
                    'date_from'  => $dateFrom,
                    'date_to'    => $item->workerRequest->date_end
                ]
            );
            
            $item->status    =   'in-cart';
            $item->save();

            $itemsAdded++; 
        }

        return [
            'itemsTotal'    =>  $itemsTotal, 
            'itemsAdded'    =>  $itemsAdded
        ];
    } 

    /**
     * Batch UnCart Items. 
     * 
     * @params array $data
     * @params int   $applicationId 
     * 
     * @return array
     */
    public function batchItemsUnCart(array $data, int $workerRequestId): array
    {
        $items  =   WorkerRequestApplication::query()
                        ->with('workerRequest')
                        ->where('worker_request_id', '=', $workerRequestId)
                        ->whereIn('id', $data['applicationIds'])
                        ->get();

        $itemsTotal     =   $items->count(); 
        $itemsRemoved   =   0; 

        foreach ($items as $item)
        {
            CompanyCart::query()
                ->select('id')
                ->where(
                    [
                        ['company_id', '=', $item->workerRequest->company_id],
                        ['worker_id',  '=', $item->worker_id]
                    ]
                )
                ->first()
                ->forceDelete();

            $item->status    =   'pending';
            $item->save();

            $itemsRemoved++;
        }

        return [
            'itemsTotal'    =>  $itemsTotal, 
            'itemsRemoved'  =>  $itemsRemoved
        ];
    } 
}