<?php

namespace App\Modules\HR\Overtime\Services;

use App\Models\User;
use App\Modules\HR\Overtime\Events\OvertimeRequestApproved;
use App\Modules\HR\Overtime\Events\OvertimeRequestRejected;
use App\Modules\HR\Overtime\Models\OvertimeRequest;
use App\Modules\HR\Overtime\Repositories\OvertimeRequestRepository;
use App\Modules\HR\Overtime\Support\OvertimeRequestStatus;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Support\Facades\DB;

class OvertimeReviewService
{
    public function __construct(
        private readonly OvertimeRequestRepository $requests,
        private readonly Dispatcher $events,
    ) {}

    public function approve(OvertimeRequest $request, User $reviewer): OvertimeRequest
    {
        return $this->decide($request, $reviewer, OvertimeRequestStatus::APPROVED);
    }

    public function reject(OvertimeRequest $request, User $reviewer): OvertimeRequest
    {
        return $this->decide($request, $reviewer, OvertimeRequestStatus::REJECTED);
    }

    private function decide(OvertimeRequest $request, User $reviewer, string $status): OvertimeRequest
    {
        return DB::transaction(function () use ($request, $reviewer, $status): OvertimeRequest {
            $locked = $this->requests->lockForTenant((int) $request->tenant_id, (int) $request->id);
            if ($locked === null) {
                abort(404);
            }
            if ($locked->status !== OvertimeRequestStatus::PENDING) {
                return $locked;
            }

            $updated = $this->requests->update($locked, [
                'status' => $status,
                'reviewed_by' => $reviewer->id,
                'reviewed_at' => now(),
            ]);

            DB::afterCommit(function () use ($updated, $reviewer, $status): void {
                if ($status === OvertimeRequestStatus::APPROVED) {
                    $this->events->dispatch(new OvertimeRequestApproved($updated, (int) $reviewer->id));
                } else {
                    $this->events->dispatch(new OvertimeRequestRejected($updated, (int) $reviewer->id));
                }
            });

            return $updated;
        });
    }
}
