<?php

namespace App\Modules\HR\Shifts\Services;

use App\Modules\HR\Employees\Models\Employee;
use App\Modules\HR\Shifts\Events\ShiftAssigned;
use App\Modules\HR\Shifts\Models\EmployeeShiftAssignment;
use App\Modules\HR\Shifts\Repositories\EmployeeShiftAssignmentRepository;
use App\Modules\HR\Shifts\Repositories\ShiftRepository;
use Carbon\CarbonImmutable;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\DB;

class EmployeeShiftAssignmentService
{
    public function __construct(
        private readonly EmployeeShiftAssignmentRepository $assignments,
        private readonly ShiftRepository $shifts,
        private readonly Dispatcher $events,
    ) {}

    public function assign(int $tenantId, int $employeeId, int $shiftId, int $actorUserId): EmployeeShiftAssignment
    {
        return DB::transaction(function () use ($tenantId, $employeeId, $shiftId, $actorUserId): EmployeeShiftAssignment {
            $employee = Employee::query()
                ->where('tenant_id', $tenantId)
                ->whereKey($employeeId)
                ->lockForUpdate()
                ->firstOrFail();
            $shift = $this->shifts->findForTenant($tenantId, $shiftId);
            if ($shift === null || ! $shift->is_active) {
                abort(404);
            }
            $now = CarbonImmutable::now();
            foreach ($this->assignments->activeRowsForEmployee($tenantId, $employeeId) as $row) {
                $this->assignments->closeAssignment($row, $now);
            }
            $row = $this->assignments->create([
                'tenant_id' => $tenantId,
                'employee_id' => $employee->id,
                'shift_id' => $shift->id,
                'starts_at' => $now,
                'ends_at' => null,
                'is_active' => true,
                'assigned_by' => $actorUserId,
            ]);
            $fresh = $row->fresh(['employee', 'shift']);
            DB::afterCommit(function () use ($fresh): void {
                $this->events->dispatch(new ShiftAssigned($fresh));
            });

            return $fresh;
        });
    }
}
