<?php

namespace App\Modules\Jobseeker\Engagement;

use App\Models\Availabilities;
use App\Models\CompanyBurdenRate;
use App\Models\CompanyExperience;
use App\Models\CompanyUser;
use App\Models\JobseekerApplicant;
use App\Models\JobseekerCompanyRefs;
use App\Models\JobseekerInvitation;
use App\Models\JobseekerNotification;
use App\Models\Notifications;
use App\Models\WorkAreaLists;
use App\Models\Worker;
use App\Models\WorkerExperience;
use App\Traits\CheckEmployeeCountTraits;
use App\Traits\GenerateJobseekerWorkerID;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use App\Events\Jobseeker\CompanyNotificationEvent;
use App\Events\Jobseeker\CreateSearchDataForNewlyAcceptedWorkerEvent;
use App\Jobs\Jobseeker\ProcessJobseekerWorkerAccount;

class Invitation
{
    use GenerateJobseekerWorkerID, CheckEmployeeCountTraits;

    public function acceptInvitation($payload)
    {
        $company_id = $payload->company_id;
        $invitation_id = $payload->invitation_id;

        /*========================================================================================================
        Check subscription
        ========================================================================================================*/
        $validate = $this->employeeCountValidation($company_id);
        if ($validate) {
            return $validate;
        }

        $jobseeker_id = Auth::user()->id;
        $jobseeker = $this->worker_details($invitation_id, $company_id);
        $email_address_to_send = $jobseeker->email_address;

        //For email
        $user_data_for_email = [
            'email_address' => $jobseeker->email_address,
            'first_name' => $jobseeker->first_name,
            'last_name' => $jobseeker->last_name,
            'company_name' => $jobseeker->company_name,
            'role' => 'jobseeker',
            'password' => '123456789',
        ];

        /* Check jobseeker if currently on a connections to company as workers*/
        $validation = $this->checkConnection($jobseeker_id);
        if ($validation) {
            return response()->json(['errors' => ['Unable to accept invitation, you are still in contract with a company.']])->setStatusCode(201);
        }

        /*========================================================================================================
        generate new worker id
        ========================================================================================================*/
        $workerGeneratedId = $this->generateWorkerId($company_id);

        /*==================================cam======================================================================
        Validate Worker id if already existed on specific company
        ========================================================================================================*/
        $validate = $this->validateWorkerGeneratedId($company_id, $workerGeneratedId);
        if (!$validate) {
            return response()->json(['errors' => ['The worker id has already been taken.']])->setStatusCode(201);
        }

        DB::beginTransaction();

        /* ADD A WORKER WORKED_ID */
        $worker_data = array(
            'worker_id' => $workerGeneratedId,
            'rate' => $jobseeker->hourly_rate,
            'flat_rate' => $jobseeker->hourly_rate,
            'date_hired' => now(),
        );
        $workerUpdate = $this->update_worker($jobseeker->id, $worker_data);

        /* CREATE WORKER AND USER DATA */
        $company_user = array(
            'worker_id' => $jobseeker->id,
            'company_id' => $company_id,
            'role_id' => 5,
            'availability_status' => 'reserved',
        );

        $saved_company_user = $this->saveCompanyUser($company_user);

        /* CREATE WORKER AVAILABILITY */
        $min_availability_date = date("Y-m-d");
        $max_availability_date = date('Y-m-d', strtotime('+10 years'));
        $worker_availability_data = array(
            'worker_id' => $jobseeker->id,
            'company_id' => $company_id,
            'worker_type' => 'inhouse',
            'rate' => $jobseeker->hourly_rate,
            'min_availability_date' => $min_availability_date,
            'max_availability_date' => $max_availability_date,
        );
        $saved_worker_availability = $this->saveWorkerAvailability($worker_availability_data);

        /* FETCH COMPANY EXPERIENCE */
        $jobseekerCompanyExpe = CompanyExperience::select('id')->where('name', $jobseeker->experience_name)->where('company_id', $company_id)->first();
        if (!$jobseekerCompanyExpe) {
            $jobseekerCompanyExpe = CompanyExperience::select('id')->where('company_id', $company_id)->first();
        }

        $burdenRateTransaction = $this->isBurdenRateExisting($company_id, $jobseekerCompanyExpe->id);

        if(!$burdenRateTransaction){
            $workerBurdenRate = array(
                'company_id' => $company_id,
                'company_experience_id' => $jobseekerCompanyExpe->id,
                'burden_rate' => 10,
                'burden_rate_type' => 'percent',
            );
            $burdenRateTransaction = $this->createBurdenRate($workerBurdenRate);
        }

        /* ADD A WORKER EXPERIENCE */
        $worker_experiences = array(
            'is_worker' => 'true',
            'worker_id' => $jobseeker->id,
            'experience_id' => $jobseekerCompanyExpe->id,
            'expertise_id' => $jobseeker->expertise_id,
            'experience' => $jobseeker->other_experience,
            'expertise' => $jobseeker->other_expertise,
            'burden_rate_id' => $burdenRateTransaction->id,
        );

        $saved_worker_experiences = $this->addWorkerExperiences($jobseeker->id, $worker_experiences);

        /* ADD A WORKER AREA LIST */
        $saved_worker_area = $this->addWorkAreaList($jobseeker->id);

        /* CREATE COMPANY REFERENCE */
        $jobseeker_company_refs = array(
            'worker_id' => $jobseeker->id,
            'company_id' => $company_id,
            'date_hired' => date("Y-m-d"),
        );
        $saved_companyRefs = $this->create_JobseekerCompanyRef($jobseeker_company_refs);

        /* CREATE COMPANY NOTIFICATION */
        $notification = $this->add_company_info($worker_data['worker_id'], $jobseeker->first_name, $jobseeker->last_name, $company_id);

        /* ACCEPT AND DECLINED THE STATUS IN THE APPLICANT */
        $accept_status = $this->accepted_applicant($jobseeker->id, $company_id);
        $declined_status = $this->declined_applicant($jobseeker->id, $jobseeker->first_name . " " . $jobseeker->last_name, $company_id);

        /* ACCEPT AND DECLINED THE STATUS IN THE INVITATIONS */
        $jobseekerInvitation = $this->acceptJobseekerInvitation($invitation_id);
        $declinedInvitation = $this->declinedInvitation($jobseeker->id);

        if (!$saved_company_user || !$workerUpdate || !$saved_worker_availability ||
            !$burdenRateTransaction || !$saved_worker_experiences || !$saved_worker_area || !$saved_companyRefs ||
            !$notification || !$accept_status || !$declined_status ||
            !$jobseekerInvitation || !$declinedInvitation) {
            DB::rollBack();
            return response()->json(['error' => 'Server error.', 'message' => 'Error, please reload the page']);
        } else {
            DB::commit();
            event(new CreateSearchDataForNewlyAcceptedWorkerEvent($jobseeker_id));
            $this->sendEmail($email_address_to_send, $user_data_for_email);
            return response()->json(['status' => 'success'])->setStatusCode(201);
        }

    }

    public function worker_details($invitation_id, $company_id)
    {

        return JobseekerNotification::select(
            'jobseeker_notifications.company_id',
            'jobseeker_invitations.id as invitation_id',
            'jobseeker_invitations.hourly_rate',
            'companies.name as company_name',
            'workers.id',
            'workers.last_name',
            'workers.first_name',
            'workers.phone_num',
            'workers.street_address',
            'workers.city',
            'workers.state',
            'workers.zipcode',
            'workers.email_address',
            'workers.password',
            'workers.email_verified_at',
            'worker_experiences.experience_id',
            'worker_experiences.expertise_id',
            'worker_experiences.experience as other_experience',
            'worker_experiences.expertise as other_expertise',
            'experiences.name as experience_name',
        )
            ->leftJoin('jobseeker_invitations', 'jobseeker_invitations.id', '=', 'jobseeker_notifications.invitation_id')
            ->leftJoin('companies', 'companies.id', '=', 'jobseeker_notifications.company_id')
            ->leftJoin('workers', 'workers.id', '=', 'jobseeker_notifications.worker_id')
            ->leftJoin('worker_experiences', 'worker_experiences.worker_id', '=', 'workers.id')
            ->leftJoin('experiences', 'worker_experiences.experience_id', '=', 'experiences.id')
            ->where('jobseeker_notifications.invitation_id', $invitation_id)
            ->where('jobseeker_notifications.company_id', $company_id)
            ->where('worker_experiences.is_worker', 'false')
            ->where('workers.id', Auth::user()->id)
            ->first();

    }

    public function update_worker($worker_id, $data)
    {
        return Worker::where('id', '=', $worker_id)->update($data);
    }

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

    public function saveWorkerAvailability($worker_availability_data)
    {
        return Availabilities::create($worker_availability_data);
    }

    public function addWorkerExperiences($worker_id, $data)
    {
        $check = WorkerExperience::where(['worker_id' => $worker_id, 'is_worker' => 'true'])->first();
        if ($check) {
            return WorkerExperience::where(['worker_id' => $worker_id, 'is_worker' => 'true'])->update($data);
        } else {
            return WorkerExperience::create($data);
        }
    }

    public function addWorkAreaList($worker_id)
    {
        $holds = WorkAreaLists::select('work_area_id', 'worker_id')->where(['worker_id' => $worker_id, 'worker_type' => 'jobseeker'])->get();
        foreach ($holds as $data) {
            WorkAreaLists::create([
                'work_area_id' => $data->work_area_id,
                'worker_id' => $worker_id,
                'worker_type' => 'worker',
            ]);
        }
        return true;
    }

    public function acceptJobseekerInvitation($id)
    {
        $data = DB::table('jobseeker_invitations')->where('id', $id)->update(['status' => 'Approved']);

        if ($data >= 0) {
            return true;
        } else {
            false;
        }

    }
    public function sendEmail($email_address, $user_data)
    {
        return ProcessJobseekerWorkerAccount::dispatch($email_address, $user_data)->onConnection('jobs_emails')->onQueue('sendemail');
    }

    public function checkConnection($worker_id)
    {
        return JobseekerCompanyRefs::where('worker_id', $worker_id)->where('date_end', null)
            ->first();
    }

    public function declinedInvitation($worker_id)
    {

        /* Retrieve the current pending invitations */
        $invitations = JobseekerInvitation::select(
            'jobseeker_notifications.invitation_id',
            'jobseeker_notifications.worker_id',
            'jobseeker_invitations.id',
            'jobseeker_invitations.status',
        )
            ->join('jobseeker_notifications', 'jobseeker_notifications.invitation_id', '=', 'jobseeker_invitations.id')
            ->where('jobseeker_notifications.worker_id', $worker_id)
            ->where('jobseeker_invitations.status', 'Pending')
            ->get();

        /* Declined the current pending invitations */
        if ($invitations) {
            foreach ($invitations as $invitation) {
                JobseekerInvitation::find($invitation->id)->update(['status' => 'Declined']);
                JobseekerNotification::where('invitation_id', $invitation->id)->update(['is_read' => true]);
            }
            return true;
        } else {
            return true;
        }
    }

    public function add_company_info($worker_id, $fname, $lname, $company_id)
    {
        $notification = Notifications::create([
            'type' => 'Jobseeker Transaction',
            'company_id' => $company_id,
            'link' => '/company/employees/reserved',
            'message' => $fname . " " . $lname . " accepted your invitation. Now saved as reserved employee with a worker ID of <b>" . $worker_id . "</b>.",
            'is_read' => 0,
        ]);
        event(new CompanyNotificationEvent($notification)); //Event for realtime notif
        return $notification;
    }

    public function accepted_applicant($worker_id, $company_id)
    {
        $data = DB::table('jobseeker_applicants')
            ->where('worker_id', '=', $worker_id)
            ->where('company_id', '=', $company_id)
            ->update(['status' => 'Accepted']);

        if ($data) {
            return true;
        } else {
            return true;
        }
    }

    public function declined_applicant($worker_id, $name, $company_id)
    {
        $datas = JobseekerApplicant::where('worker_id', $worker_id)->where('company_id', '!=', $company_id)->whereNotIn('status', ['Accepted', 'Declined'])->get();

        if ($datas) {
            foreach ($datas as $invitation) {
                JobseekerApplicant::where('id', $invitation->id)->update(['status' => 'Declined']);
                $this->company_decline_notif($name, $invitation->company_id);
            }
            return true;
        } else {
            return true;
        }
    }

    public function create_JobseekerCompanyRef($data)
    {
        return JobseekerCompanyRefs::create($data);
    }

    public function company_decline_notif($name, $company_id)
    {
        $notification = Notifications::create([
            'type' => 'Jobseeker Transaction',
            'company_id' => $company_id,
            'link' => '/company/job-seeker-applicant',
            'message' => $name . " declined your invitation.",
            'is_read' => 0,
        ]);
        event(new CompanyNotificationEvent($notification)); //Event for realtime notif
        return $notification;
    }

    public function isBurdenRateExisting($companyID, $experienceID){
        return CompanyBurdenRate::where('company_id', $companyID)
            ->where('company_experience_id', $experienceID)
            ->first();
    }

    public function createBurdenRate($data)
    {
        return CompanyBurdenRate::create($data);
    }

}
