<?php

namespace App\Modules\HR\Attendance\Services\ExceptionDetection;

use App\Modules\HR\Attendance\Contracts\AttendanceExceptionDetectionRule;
use App\Modules\HR\Attendance\Models\AttendanceRecord;
use App\Modules\HR\Attendance\Repositories\AttendanceExceptionRepository;
use App\Modules\HR\Attendance\Support\AttendanceExceptionDedupeKey;
use App\Modules\HR\Attendance\Support\AttendanceExceptionSeverity;
use App\Modules\HR\Attendance\Support\AttendanceExceptionType;
use App\Modules\HR\Employees\Models\Employee;
use App\Modules\HR\Shifts\Services\ShiftResolutionService;
use Carbon\CarbonImmutable;

class OutsideShiftExceptionRule implements AttendanceExceptionDetectionRule
{
    public function __construct(
        private readonly AttendanceExceptionRepository $exceptions,
        private readonly ShiftResolutionService $shifts,
    ) {}

    public function run(int $tenantId, CarbonImmutable $from, CarbonImmutable $to): int
    {
        $created = 0;

        AttendanceRecord::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('attendance_date', [$from->toDateString(), $to->toDateString()])
            ->whereNotNull('check_in_at')
            ->with('employee')
            ->chunkById(500, function ($chunk) use ($tenantId, &$created): void {
                foreach ($chunk as $record) {
                    $emp = $record->employee;
                    if (! $emp instanceof Employee) {
                        continue;
                    }
                    $checkIn = CarbonImmutable::parse($record->check_in_at->toIso8601String());
                    $ymd = $record->attendance_date->toDateString();
                    if (! $this->shifts->violatesAssignedShiftWindow($tenantId, (int) $emp->id, $ymd, $checkIn)) {
                        continue;
                    }
                    $key = AttendanceExceptionDedupeKey::forRecordType(
                        $tenantId,
                        (int) $record->employee_id,
                        (int) $record->id,
                        AttendanceExceptionType::OUTSIDE_SHIFT,
                        $ymd,
                    );
                    $created += (int) $this->exceptions->tryInsert(
                        $tenantId,
                        (int) $record->employee_id,
                        (int) $record->id,
                        AttendanceExceptionType::OUTSIDE_SHIFT,
                        AttendanceExceptionSeverity::MEDIUM,
                        $key,
                        ['attendance_date' => $ymd],
                    );
                }
            });

        return $created;
    }
}
