<?php

namespace App\Modules\Company\WorkerRequest;

use App\Events\Company\WorkerRequest\NotificationOnNewAddedWorkerRequestEvent;
use App\Models\WorkerRequest;
use App\Models\ZipcodeRange;
use App\Models\Company;
use App\Models\Notifications;
use App\Models\State;
use App\Http\Resources\Company\WorkerRequest\WorkerRequestManagementCollection;
use App\Http\Resources\Company\WorkerRequest\WorkerRequestManagementResource;
use App\Traits\UserTraits;
use App\Traits\CustomPaginationTrait;
use App\Traits\CreateUserActionTraits;

use Illuminate\Database\Eloquent\Builder; 

use Carbon\Carbon;

class WorkerRequestManagementModule
{
    /** Traits */
    use CustomPaginationTrait;
    use CreateUserActionTraits; 
    use UserTraits;

    /**
     * Fetch Items
     * 
     * @param array $data 
     * 
     * @return WorkerRequestManagementCollection 
     */
    public function fetchItems(array $data): WorkerRequestManagementCollection
    {
        $currentUser =  $this->getCurrentUser();
        $companyId   =  $currentUser->company_id;

        $items      =   WorkerRequest::query()
                            ->with('applications', 'company', 'expertise', 'workAreas', 'zipCodeRange', 'zipCodeRange.state')
                            ->withCount(
                                [
                                    'applications' => function (Builder $query)
                                    {
                                        $query->where('status', '=', 'pending');
                                    }
                                ]
                            )
                            ->where('company_id', '=', $companyId)
                            ->when(
                                isset($data['status']),
                                function ($query) use ($data) 
                                {
                                    $query->where('status', '=', $data['status']);
                                }
                            )
                            ->when(
                                isset($data['dateStart']) && isset($data['dateEnd']),
                                function ($query) use ($data) 
                                {
                                    $dateStart  =  Carbon::parse($data['dateStart'])->format('Y-m-d');
                                    $dateEnd    =  Carbon::parse($data['dateEnd'])->format('Y-m-d');

                                    $query->whereDate('date_start', '>=', $dateStart)
                                        ->whereDate('date_end', '<=', $dateEnd);
                                }
                            )
                            // ->when(
                            //     isset($data['state']) && isset($data['city']),
                            //     function ($query) use ($data) 
                            //     {
                            //         //! calculate for zipcode here
                            //         $stateName          =   "{$data['state']}%";
                            //         $cityName           =   "{$data['city']}%";

                            //         $stateIds           =   State::query()
                            //             ->select('id')
                            //             ->where('name', 'LIKE', $stateName)
                            //             ->where('city', 'LIKE', $cityName)
                            //             ->pluck('id');

                            //         $zipCodeRangeIds    =   ZipcodeRange::query()
                            //             ->select('id')
                            //             ->whereIn('state_id', $stateIds)
                            //             ->pluck('id');

                            //         $query->whereIn('zipcode_range_id', $zipCodeRangeIds);
                            //     }
                            // )
                            ->when(
                                isset ($data['state']),
                                function ($query) use ($data) 
                                {
                                    //! calculate for zipcode here
                                    $stateName          =   "{$data['state']}%";

                                    $stateIds           =   State::query()
                                        ->select('id')
                                        ->where('name', 'LIKE', $stateName)
                                        ->pluck('id');

                                    $zipCodeRangeIds    =   ZipcodeRange::query()
                                        ->select('id')
                                        ->whereIn('state_id', $stateIds)
                                        ->pluck('id');

                                    $query->whereIn('zipcode_range_id', $zipCodeRangeIds);
                                }
                            )
                            ->when(
                                isset ($data['city']),
                                function ($query) use ($data) 
                                {
                                    //! calculate for zipcode here
                                    $cityName           =   "{$data['city']}%";

                                    $stateIds           =   State::query()
                                        ->select('id')
                                        ->where('city', 'LIKE', $cityName)
                                        ->pluck('id');

                                    $zipCodeRangeIds    =   ZipcodeRange::query()
                                        ->select('id')
                                        ->whereIn('state_id', $stateIds)
                                        ->pluck('id');

                                    $query->whereIn('zipcode_range_id', $zipCodeRangeIds);
                                }
                            )
                            ->when(
                                isset($data['expertise']),
                                function ($query) use ($data) 
                                {
                                    $query->where('expertise_id', '=', $data['expertise']);
                                }
                            )
                            ->when(
                                isset($data['workAreas']),
                                function ($query) use ($data) 
                                {
                                    $workAreas = $data['workAreas'];

                                    $query->whereHas(
                                        'workAreas',
                                        function ($q) use ($workAreas) {
                                            $q->whereIn('work_areas.id', $workAreas);
                                        }
                                    );
                                }
                            )
                            ->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 "workerRequestDateStart":
                            return $query->date_start;
                            break;

                        case "workerRequestStateName":
                            return $query->zipCodeRange->state->name;
                            break;

                        case "workerRequestExperience":
                            return $query->experience;
                            break;

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

                        case "workerRequestStatus":
                            return $query->status;
                            break;

                        case "workerRequestUpdatedAt":
                            return $query->updated_at;
                            break;

                        case "workerRequestZipCodeRadius":
                            return $query->zip_code_radius;
                            break;

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

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

        return new WorkerRequestManagementCollection($items);
    }

    /**
     * Fetch Item 
     * 
     * @param int $id 
     * 
     * @return  WorkerRequestManagementResource
     */
    public function fetchItem(int $id): WorkerRequestManagementResource
    {
        $currentUser =  $this->getCurrentUser();
        $companyId   =  $currentUser->company_id;

        $item   =   WorkerRequest::query()
                        ->with('applications', 'company', 'expertise', 'workAreas', 'zipCodeRange', 'zipCodeRange.state') //? Worker Aresas of Work Bug Here
                        ->withCount('applications')
                        ->where('company_id', '=', $companyId)
                        ->findOrFail($id);

        return WorkerRequestManagementResource::make($item);
    }

    /**
     * Create Item 
     * 
     * @param array $data 
     * 
     * @return void
     */
    public function createItem(array $data): void
    {
        $currentUser =  $this->getCurrentUser();
        $companyId   =  $currentUser->company_id;

        Company::findOrFail($companyId);

        //! calculate for zipcode here
        $stateName          =   "{$data['state']}%";
        $cityName           =   "{$data['city']}%";

        $stateId            =   State::query()
                                    ->where('name', 'LIKE', $stateName)
                                    ->where('city', 'LIKE', $cityName)
                                    ->firstOrFail('id')
                                    ->id;

        $zipCodeRange       =   ZipcodeRange::query()
                                    ->where('state_id', '=', $stateId)
                                    ->firstOrFail();

        $dateStart  =  Carbon::parse($data['dateStart'])->format('Y-m-d');
        $dateEnd    =  Carbon::parse($data['dateEnd'])->format('Y-m-d');

        $workerRequest = WorkerRequest::create(
            [
                'company_id'                          => $companyId,
                'expertise_id'                        => $data['expertise'],
                'experience'                          => $data['experience'],
                'zipcode_range_id'                    => $zipCodeRange->id,
                'date_start'                          => $dateStart,
                'date_end'                            => $dateEnd,
                'description'                         => $data['description'],
                'zip_code_radius'                     => $data['stateRadius'],
                'number_of_applicants_to_be_borrowed' => $data['numberOfApplicantsToBeBorrowed']
                //! 'on_success_action' => $data['onSuccessAction']
            ]
        );

        $workerRequest->workAreas()->attach($data['workAreas']);

        $zipcodeNotificationData = (object)
        [
            'id'               => $workerRequest->id,
            'company_id'       => $companyId,
            'expertise_id'     => $data['expertise'],
            'experience'       => $data['experience'],
            'zipcode'          => $zipCodeRange->zipcode,
            'zipcode_range_id' => $zipCodeRange->id,
            'range'            => $data['stateRadius'],
        ];

        event(new NotificationOnNewAddedWorkerRequestEvent($zipcodeNotificationData));

        $this->createUserAction('Created an employee request'); 
    }

    /**
     * Update Item 
     * 
     * @param  array $data 
     * @param  int   $id    
     * 
     * @return void
     */
    public function updateItem(array $data, int $id): void
    {
        $item = WorkerRequest::findOrFail($id);

        $item->update(
            [
                'description'       => $data['description'],
                //! 'on_success_action' => $data['onSuccessAction']
            ]
        );

        $this->createUserAction('Updated the job description of an employee request');
    }

    /**
     * Delete Item 
     * 
     * @param  int   $id    

     * @return void
     */
    public function deleteItem(int $id): void
    {
        $item = WorkerRequest::findOrFail($id);

        $item->delete();

        $this->createUserAction('Deleted an employee request');
    }

    /**
     * Activate Item
     * 
     * @param int $id 
     * 
     * @return void 
     */
    public function itemActivate(int $id): void
    {
        WorkerRequest::findOrFail($id)
            ->update(
                [
                    'status' => 'active'
                ]
            );

        $this->createUserAction('Activated an employee request');
    }

    /**
     * Disable Item
     * 
     * @param int $id 
     * 
     * @return void 
     */
    public function itemDisable(int $id): void
    {
        $item   =   WorkerRequest::query()
                        ->with('applications')
                        ->findOrFail($id);

        $item->status = 'disabled';

        //! Delete all applications, we do not need to keep them. 
        $item->applications()->delete();

        $item->save();

        $this->createUserAction('Marked an employee request as disabled');
    }

    /**
     * Complete Item
     * 
     * @param int $id 
     * 
     * @return void 
     */
    public function itemComplete(int $id): void
    {
        $item   =   WorkerRequest::query()
                        ->with('applications')
                        ->findOrFail($id);

        $item->status = 'completed';

        //! Delete all applications, we do not need to keep them. 
        $item->applications()->delete();
        
        $item->save();

        $this->createUserAction('Marked an employee request as completed');
    }

    /**
     * Batch Activate Items 
     * 
     * @param array $data 
     * 
     * @return void
     */
    public function batchItemsActivate(array $data): void
    {
        WorkerRequest::query()
            ->select('id')
            ->whereIn('id', $data['workerRequestIds'])
            ->update(
                [
                    'status'    =>  'active'
                ]
            );

        $actionMessage = count($data['workerRequestIds']) > 1 ? "Activated multiple employee requests" : "Activated an employee request";

        $this->createUserAction($actionMessage);
    }

    /**
     * Batch Disable Items 
     * 
     * @param array $data 
     * 
     * @return void
     */
    public function batchItemsDisable(array $data): void
    {
        $items  =   WorkerRequest::query()
                        ->with('applications')
                        ->select('id')
                        ->whereIn('id', $data['workerRequestIds'])
                        ->get();

        foreach ($items as $item)
        {
            $item->status = 'disabled';

            //! Delete all applications, we do not need to keep them. 
            $item->applications()->delete();

            $item->save();
        }

        $actionMessage = count($data['workerRequestIds']) > 1 ? "Marked multiple employee requests as disabled" : "Marked an employee request as disabled";

        $this->createUserAction($actionMessage);
    }

    /**
     * Batch Complete Items 
     * 
     * @param array $data 
     * 
     * @return void
     */
    public function batchItemsComplete(array $data): void
    {
        $items  =   WorkerRequest::query()
                        ->with('applications')
                        ->select('id')
                        ->whereIn('id', $data['workerRequestIds'])
                        ->get();

        foreach ($items as $item) 
        {
            $item->status = 'completed';

            //! Delete all applications, we do not need to keep them. 
            $item->applications()->delete();

            $item->save();
        }

        $actionMessage = count($data['workerRequestIds']) > 1 ? "Marked multiple employee requests as completed" : "Marked an employee request as completed";

        $this->createUserAction($actionMessage);
    }

    /**
     * Batch Delete Items 
     * 
     * @param array $data 
     * 
     * @return void
     */
    public function batchItemsDelete(array $data): void
    {
        WorkerRequest::query()
            ->select('id')
            ->whereIn('id', $data['workerRequestIds'])
            ->delete();

        $actionMessage = count($data['workerRequestIds']) > 1 ? "Deleted multiple employee requests" : "Deleted an employee request";

        $this->createUserAction($actionMessage);
    }

    /**
     * Set Items to expire if Date End exceeds current Date
     * 
     * @return void 
     */
    public function itemsSetStatusToExpire(): void
    {
        $currentDate = Carbon::now();

        $items  =   WorkerRequest::query()
                        ->select('id', 'status', 'date_end', 'company_id')
                        ->with('applications')
                        ->whereNotIn('status', ['expired', 'completed'])
                        ->whereDate('date_end', '<=', $currentDate)
                        ->get();

        foreach ($items as $item) 
        {
            $item->status = 'expired';

            //! Delete all applications, we do not need to keep them. 
            $item->applications()->delete();

            $item->save();

            $notificationData = array(
                'type'       => 'Employee Request',
                'company_id' => $item->company_id,
                'message'    => "An Employee Request has been expired.",
                'link'       => '/company/worker-request/manage',
                "is_read"    => false,
            );

            Notifications::create($notificationData);
        }
    }
}
