<?php

namespace App\Modules\HR\Leave\Listeners;

use App\Models\User;
use App\Modules\HR\Employees\Models\Employee;
use App\Modules\HR\Leave\Events\LeaveRequestHrApproved;
use App\Modules\HR\Leave\Events\LeaveRequestManagerApproved;
use App\Modules\HR\Leave\Events\LeaveRequestRejected;
use App\Modules\HR\Leave\Events\LeaveRequestSubmitted;
use App\Modules\HR\Leave\Models\LeaveRequest;
use App\Modules\HR\Leave\Notifications\LeaveRequestApprovedNotification;
use App\Modules\HR\Leave\Notifications\LeaveRequestRejectedNotification;
use App\Modules\HR\Leave\Notifications\LeaveRequestSubmittedNotification;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Notification as NotificationFacade;

class LeaveApproverNotificationListener
{
    public function handleSubmitted(LeaveRequestSubmitted $event): void
    {
        $leave = $event->leaveRequest;
        $tenantId = (int) $leave->tenant_id;

        $manager = $this->managerUser($leave);
        if ($manager !== null) {
            $manager->notify(new LeaveRequestSubmittedNotification($leave));
        }

        $fallbackApprovers = $this->managerApprovers($tenantId);
        if ($manager === null && $fallbackApprovers->isNotEmpty()) {
            NotificationFacade::send($fallbackApprovers, new LeaveRequestSubmittedNotification($leave));
        }
    }

    public function handleManagerApproved(LeaveRequestManagerApproved $event): void
    {
        $leave = $event->leaveRequest;
        $tenantId = (int) $leave->tenant_id;
        $recipients = $this->hrApprovers($tenantId);
        if ($recipients->isNotEmpty()) {
            NotificationFacade::send($recipients, new LeaveRequestSubmittedNotification($leave));
        }
    }

    public function handleHrApproved(LeaveRequestHrApproved $event): void
    {
        $leave = $event->leaveRequest;
        $this->notifyEmployeeAndRequester($leave, new LeaveRequestApprovedNotification($leave));
    }

    public function handleRejected(LeaveRequestRejected $event): void
    {
        $leave = $event->leaveRequest;
        $this->notifyEmployeeAndRequester($leave, new LeaveRequestRejectedNotification($leave));
    }

    private function notifyEmployeeAndRequester(LeaveRequest $leave, Notification $notification): void
    {
        $leave->loadMissing('employee.user');

        $targets = collect();

        $employeeUser = $leave->employee?->user;
        if ($employeeUser instanceof User) {
            $targets->push($employeeUser);
        }

        $requesterId = $leave->requested_by;
        if ($requesterId !== null) {
            $u = User::query()->find((int) $requesterId);
            if ($u !== null) {
                $targets->push($u);
            }
        }

        $unique = $targets->unique('id')->values();
        if ($unique->isNotEmpty()) {
            NotificationFacade::send($unique, $notification);
        }
    }

    /**
     * @return \Illuminate\Support\Collection<int, User>
     */
    private function managerApprovers(int $tenantId)
    {
        return User::query()
            ->where('tenant_id', $tenantId)
            ->where('is_admin', false)
            ->get()
            ->filter(fn (User $u): bool => $u->can('leave.approve.manager'));
    }

    /**
     * @return \Illuminate\Support\Collection<int, User>
     */
    private function hrApprovers(int $tenantId)
    {
        return User::query()
            ->where('tenant_id', $tenantId)
            ->where('is_admin', false)
            ->get()
            ->filter(fn (User $u): bool => $u->can('leave.approve.hr'));
    }

    private function managerUser(LeaveRequest $leave): ?User
    {
        $leave->loadMissing('employee');
        $employee = $leave->employee;
        if (! $employee instanceof Employee) {
            return null;
        }
        if ($employee->manager_employee_id === null) {
            return null;
        }
        $manager = Employee::query()->whereKey((int) $employee->manager_employee_id)->first();
        if ($manager === null) {
            return null;
        }

        return $manager->user;
    }
}
