<?php

namespace App\Services\Hr;

use App\Enums\Permission;
use App\Enums\Role;
use App\Models\Attendance;
use App\Models\Employee;
use App\Models\LeaveRequest;
use App\Models\User;
use App\Services\Concerns\ResolvesAuthenticatedEmployee;
use App\Services\Media\MediaService;
use App\Services\System\PushNotificationService;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;

class AttendanceService
{
    use ResolvesAuthenticatedEmployee;

    public function __construct(
        private readonly MediaService $mediaService,
        private readonly PushNotificationService $pushNotificationService,
    ) {}

    public function listForCalendar(
        CarbonInterface $start,
        CarbonInterface $end,
        ?int $scopedEmployeeId = null,
        bool $hasScopedAccess = true,
    ): Collection {
        return Attendance::query()
            ->with(['employee.user.profile', 'media'])
            ->when(! $hasScopedAccess, function (Builder $query): void {
                $query->whereRaw('1 = 0');
            })
            ->when($hasScopedAccess && $scopedEmployeeId !== null, function (Builder $query) use ($scopedEmployeeId): void {
                $query->where('employee_id', $scopedEmployeeId);
            })
            ->whereDate('attendance_date', '>=', $start->toDateString())
            ->whereDate('attendance_date', '<', $end->toDateString())
            ->orderBy('attendance_date')
            ->orderBy('check_in_at')
            ->get();
    }

    public function todayAttendanceForEmployee(Employee $employee): ?array
    {
        $attendance = Attendance::query()
            ->with('media')
            ->where('employee_id', $employee->id)
            ->whereDate('attendance_date', today())
            ->first();

        return $attendance?->toArray();
    }

    public function isOnLeaveToday(Employee $employee): bool
    {
        return LeaveRequest::query()
            ->approved()
            ->where('employee_id', $employee->id)
            ->whereDate('start_date', '<=', today())
            ->whereDate('end_date', '>=', today())
            ->exists();
    }

    public function checkIn(array $validated, User $user): void
    {
        $employee = $this->resolveAuthenticatedEmployee($user);

        $existing = Attendance::query()
            ->where('employee_id', $employee->id)
            ->whereDate('attendance_date', today())
            ->exists();

        if ($existing) {
            throw ValidationException::withMessages([
                'attendance' => 'Anda sudah melakukan presensi masuk hari ini.',
            ]);
        }

        $attendance = Attendance::create([
            'employee_id' => $employee->id,
            'attendance_date' => today(),
            'check_in_at' => now(),
            'check_in_latitude' => $validated['latitude'],
            'check_in_longitude' => $validated['longitude'],
        ]);

        $this->mediaService->addBase64Image(
            $attendance,
            $validated['photo'],
            'checkin',
            'checkin',
        );

        $this->pushNotificationService->sendToRoles(
            '⏰ Presensi Masuk',
            "Karyawan {$user->profile?->full_name} melakukan presensi masuk.",
            ['owner', 'developer'],
            route('admin.hr.attendances.index'),
        );
    }

    public function checkOut(array $validated, User $user): void
    {
        $employee = $this->resolveAuthenticatedEmployee($user);

        $attendance = Attendance::query()
            ->where('employee_id', $employee->id)
            ->whereDate('attendance_date', today())
            ->first();

        if ($attendance === null) {
            throw ValidationException::withMessages([
                'attendance' => 'Anda belum melakukan presensi masuk hari ini.',
            ]);
        }

        if ($attendance->check_out_at !== null) {
            throw ValidationException::withMessages([
                'attendance' => 'Anda sudah melakukan presensi pulang hari ini.',
            ]);
        }

        $checkOutAt = now();
        $workDurationMinutes = (int) $attendance->check_in_at->diffInMinutes($checkOutAt);

        $attendance->update([
            'check_out_at' => $checkOutAt,
            'check_out_latitude' => $validated['latitude'],
            'check_out_longitude' => $validated['longitude'],
            'work_duration_minutes' => $workDurationMinutes,
        ]);

        $this->mediaService->addBase64Image(
            $attendance,
            $validated['photo'],
            'checkout',
            'checkout',
        );

        $this->pushNotificationService->sendToRoles(
            '⏰ Presensi Pulang',
            "Karyawan {$user->profile?->full_name} melakukan presensi pulang (Durasi kerja: ".round($workDurationMinutes / 60, 1).' jam).',
            ['owner', 'developer'],
            route('admin.hr.attendances.index'),
        );
    }

    public function delete(Attendance $attendance): void
    {
        $attendance->loadMissing('employee.user.profile');
        $employeeName = $attendance->employee?->user?->profile?->full_name;
        $date = $attendance->attendance_date;

        $attendance->clearMediaCollection('checkin');
        $attendance->clearMediaCollection('checkout');
        $attendance->delete();

        $this->pushNotificationService->sendToRoles(
            '🗑️ Presensi Dihapus',
            "Data presensi {$employeeName} tanggal {$date} telah dihapus.",
            ['owner', 'developer'],
            route('admin.hr.attendances.index'),
        );
    }

    public function resolveAttendanceScope(User $user): array
    {
        $employee = $user->employee;
        $isManager = $user->hasAnyRole([Role::OWNER->value, Role::DEVELOPER->value, Role::DIREKTUR->value]);

        return [
            'scopedEmployeeId' => $isManager ? null : $employee?->id,
            'hasScopedAccess' => $isManager || $employee !== null,
            'isManager' => $isManager,
            'employee' => $employee,
            'canCheckIn' => $user->can(Permission::ATTENDANCES_CREATE->value) && $employee !== null,
        ];
    }

    public function indexPageData(User $user, CarbonInterface $start, CarbonInterface $end): array
    {
        $scope = $this->resolveAttendanceScope($user);
        $employee = $scope['employee'];

        return [
            'attendances' => $this->listForCalendar(
                $start,
                $end,
                $scope['scopedEmployeeId'],
                $scope['hasScopedAccess'],
            ),
            'todayAttendance' => $employee
                ? $this->todayAttendanceForEmployee($employee)
                : null,
            'isOnLeave' => $employee
                ? $this->isOnLeaveToday($employee)
                : false,
            'canCheckIn' => $scope['canCheckIn'],
            'canManageAll' => $scope['isManager'],
            'calendarRange' => [
                'start' => $start->toDateString(),
                'end' => $end->toDateString(),
            ],
        ];
    }
}
