<?php

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

use App\Modules\HR\Attendance\Contracts\AttendanceExceptionDetectionRule;
use App\Modules\HR\Attendance\Models\BiometricLog;
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 Carbon\CarbonImmutable;

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

    public function run(int $tenantId, CarbonImmutable $from, CarbonImmutable $to): int
    {
        $counts = [];
        BiometricLog::query()
            ->where('tenant_id', $tenantId)
            ->whereBetween('punch_time', [$from->startOfDay(), $to->copy()->endOfDay()])
            ->orderBy('id')
            ->chunk(3000, function ($chunk) use (&$counts): void {
                foreach ($chunk as $log) {
                    $slot = $log->punch_time->format('Y-m-d H:i');
                    $k = $log->employee_fingerprint_id.'|'.$slot;
                    $counts[$k] = ($counts[$k] ?? 0) + 1;
                }
            });

        $created = 0;
        foreach ($counts as $k => $c) {
            if ($c <= 1) {
                continue;
            }
            [$fp, $slot] = explode('|', $k, 2);
            $employee = Employee::query()
                ->where('tenant_id', $tenantId)
                ->where('fingerprint_id', $fp)
                ->first();
            if ($employee === null) {
                continue;
            }
            $dedupe = AttendanceExceptionDedupeKey::forDuplicatePunch($tenantId, $fp, $slot);
            $created += (int) $this->exceptions->tryInsert(
                $tenantId,
                (int) $employee->id,
                null,
                AttendanceExceptionType::DUPLICATE_PUNCH,
                AttendanceExceptionSeverity::MEDIUM,
                $dedupe,
                ['fingerprint_id' => $fp, 'minute_slot' => $slot, 'punch_count' => $c],
            );
        }

        return $created;
    }
}
