<?php

namespace App\Modules\Company\Whiteboard;

use App\Models\AssignedAvailability;
use App\Models\AssignedWorkers;
use App\Models\ProjectWorkersTimesheet;
use App\Models\MobileNotification;
use App\Events\Company\ProjectWorkerAssignmentEvent;
use App\Traits\CreateUserActionTraits;
use App\Traits\ProjectWorkersTimesheetHelperTrait;
use Illuminate\Support\Facades\DB;
use App\Traits\CreateMobileNotificationTraits;

class SaveDraftEmployees
{
    use ProjectWorkersTimesheetHelperTrait, CreateUserActionTraits, CreateMobileNotificationTraits;

    public function save($id)
    {
        // Start Transaction
        DB::beginTransaction();
        $saveProjectWorkersTimeSheetTransaction = 1;
        $updateDraftEmployeeTransaction = 1;

        // Fetch assigned workers
        $assignedWorkersData = $this->fetchAssignedWorkers($id);

        $this->setProjectId($id);
        $previous_workers_set = $this->getWorkersOnProject();

        // Save Project Worker Timesheet
        foreach ($assignedWorkersData as $assignedWorkerData) {
            $assignedWorkerId = $assignedWorkerData->assigned_worker_id;
            $workerId = $assignedWorkerData->worker_id;
            $assignedAvailabilityId = $assignedWorkerData->assigned_availability_id;
            $workerType = $assignedWorkerData->worker_type;
            $rate = $assignedWorkerData->rate;
            $startDate = $assignedWorkerData->start_date;
            $endDate = $assignedWorkerData->end_date;
            $projectId = $assignedWorkerData->project_id;
            $clockIn = $assignedWorkerData->clock_in;
            $clockOut = $assignedWorkerData->clock_out;
            $firstName = $assignedWorkerData->first_name;
            $lastName = $assignedWorkerData->last_name;
            $fullName = $firstName . ' ' . $lastName;

            // Combine Continuos Duration Start
            $startDateMinus1Day = date('Y-m-d', (strtotime('-1 day', strtotime($startDate))));
            $endDatePlus1Day = date('Y-m-d', (strtotime('+1 day', strtotime($endDate))));
            $assignedWorkerWithContinuousDurationFromStartDate = AssignedWorkers::select(
                'assigned_availability.id',
                'assigned_availability.start_date',
                'assigned_availability.end_date',
                'assigned_workers.id as assigned_worker_id')
                ->join('assigned_availability', 'assigned_availability.assigned_worker_id', '=', 'assigned_workers.id')
                ->where('assigned_workers.worker_id', $workerId)
                ->where('assigned_workers.project_id', $projectId)
                ->where('assigned_availability.rate', $rate)
                ->where('assigned_availability.is_draft', 0)
                ->where(function ($query) use ($startDateMinus1Day, $endDatePlus1Day) {
                    $query->where('assigned_availability.start_date', $endDatePlus1Day)
                        ->orWhere('assigned_availability.end_date', $startDateMinus1Day);
                })
                ->orderBy('assigned_availability.start_date', 'asc')
                ->get();
            if (!$assignedWorkerWithContinuousDurationFromStartDate->isEmpty()) {
                $assignedAvailabilityData = array();
                if ($assignedWorkerWithContinuousDurationFromStartDate->count() == 1) {
                    if ($assignedWorkerWithContinuousDurationFromStartDate[0]['start_date'] > $startDate) {
                        // Concat duration from the start_date
                        $assignedAvailabilityData = array(
                            'start_date' => $startDate,
                        );
                    } else {
                        // Concat duration from the end_date
                        $assignedAvailabilityData = array(
                            'end_date' => $endDate,
                        );
                    }
                    AssignedAvailability::where('assigned_availability.id', $assignedWorkerWithContinuousDurationFromStartDate[0]['id'])->update($assignedAvailabilityData);
                    AssignedAvailability::where('assigned_availability.id', $assignedAvailabilityId)->delete();
                    AssignedWorkers::where('assigned_workers.id', $assignedWorkerId)->delete();
                } else {
                    // Concat duration from the end_date of next row data and delete the next data that is continuous
                    $assignedAvailabilityData = array(
                        'end_date' => $assignedWorkerWithContinuousDurationFromStartDate[1]['end_date'],
                    );
                    AssignedAvailability::where('assigned_availability.id', $assignedWorkerWithContinuousDurationFromStartDate[0]['id'])->update($assignedAvailabilityData);
                    AssignedAvailability::where('assigned_availability.id', $assignedWorkerWithContinuousDurationFromStartDate[1]['id'])->delete();
                    AssignedWorkers::where('assigned_workers.id', $assignedWorkerWithContinuousDurationFromStartDate[1]['assigned_worker_id'])->delete();
                    AssignedAvailability::where('assigned_availability.id', $assignedAvailabilityId)->delete();
                    AssignedWorkers::where('assigned_workers.id', $assignedWorkerId)->delete();
                }
            }

            // Combine Continuos Duration End
            $updateDraftEmployee = $this->updateDraftEmployee($assignedAvailabilityId);
            if (!$updateDraftEmployee) {
                $updateDraftEmployeeTransaction = 0;
            }
            for ($date = $startDate; $date <= $endDate; $date = date('Y-m-d', strtotime($date . '+1 day'))) {
                $projectWorkersTimesheetData = array(
                    'project_worker_id' => $assignedWorkerId,
                    'worker_id' => $workerId,
                    'worker_type' => $workerType,
                    'rate' => $rate,
                    'total_billing_per_day' => 0,
                    'total_hours' => 0,
                    'date' => $date,
                    'time_in' => $clockIn,
                    'time_out' => $clockOut,
                    'status' => 'pending',
                    'request_modification_message' => null,
                    'requested_by' => $fullName,
                    'approved_by' => null,
                );
                $saveProjectWorkersTimeSheet = ProjectWorkersTimesheet::create($projectWorkersTimesheetData);
                if (!$saveProjectWorkersTimeSheet) {
                    $saveProjectWorkersTimeSheetTransaction = 0;
                }
            }

            $this->createUserAction("Added " . $firstName . " " . $lastName . " to project " . $assignedWorkerData->name . " with duration date of " . $startDate . " to " . $endDate);
            $mobileNotificationData = array(
                'worker_id' => $workerId,
                'message' => 'You have been assigned to '. ucfirst($assignedWorkerData->name) . " with a duration date of " . $startDate . " to " . $endDate . ".",
                'type' => 'worker',
                'link' => '/worker/tasks',
            );
            $this->createMobileNotification($mobileNotificationData);
        }

        // If there's an error or queries don't do their job, rollback!
        if (!$saveProjectWorkersTimeSheetTransaction ||
            !$updateDraftEmployeeTransaction) {
            DB::rollBack();
            return response()->json(['error' => 'Server error.',
                'saveProjectWorkersTimeSheetTransaction' => $saveProjectWorkersTimeSheetTransaction,
                'updateDraftEmployeeTransaction' => $updateDraftEmployeeTransaction]);
        } else {
            event(new ProjectWorkerAssignmentEvent($this->identifyNewWorkers($this->getUpdatedWorkers($previous_workers_set))));
            DB::commit();
            return response()->json(['success' => 'Data updated successfully.'])->setStatusCode(201);
        }
    }

    public function fetchAssignedWorkers($project_id)
    {
        return AssignedWorkers::select(
            'assigned_workers.id as assigned_worker_id',
            'assigned_workers.worker_id',
            'assigned_availability.id as assigned_availability_id',
            'assigned_availability.worker_type',
            'assigned_availability.rate',
            'assigned_availability.start_date',
            'assigned_availability.end_date',
            'projects.id as project_id',
            'projects.clock_in',
            'projects.clock_out',
            'projects.name',
            'workers.first_name',
            'workers.last_name')
            ->join('projects', 'projects.id', '=', 'assigned_workers.project_id')
            ->join('assigned_availability', 'assigned_availability.assigned_worker_id', '=', 'assigned_workers.id')
            ->join('workers', 'workers.id', '=', 'assigned_workers.worker_id')
            ->where('assigned_workers.project_id', $project_id)
            ->where('assigned_availability.is_draft', 1)
            ->get();
    }

    public function updateDraftEmployee($assignedAvailabilityId)
    {
        $assignedAvailabilityData = array(
            'is_draft' => 0,
        );
        return AssignedAvailability::where('assigned_availability.id', $assignedAvailabilityId)->withTrashed()->update($assignedAvailabilityData);
    }
}
