<?php

namespace App\Services\Biometric;

use App\Models\AttendancePunch;
use App\Models\BiometricDevice;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;

class DeviceMonthlyPunchSyncService
{
    public function __construct(
        protected ZktecoPunchIngestService $ingestService,
        protected BiometricFirstInLastOutService $filoService,
    ) {}

    /**
     * Reprocess punches for a device within a calendar month.
     * both-mode: reset all punches and recalculate FILO days.
     * login/logout: reset pending/error/ignored and re-apply gate path.
     *
     * @return array{month: string, reset: int, applied: int, ignored: int, error: int, pending: int, total: int}
     */
    public function syncMonth(BiometricDevice $device, Carbon $month): array
    {
        $from = $month->copy()->startOfMonth();
        $to = $month->copy()->endOfMonth();
        $isFilo = ($device->punch_mode ?: BiometricDevice::PUNCH_MODE_BOTH) === BiometricDevice::PUNCH_MODE_BOTH;

        $query = AttendancePunch::withoutGlobalScopes()
            ->where('company_id', $device->company_id)
            ->where('biometric_device_id', $device->id)
            ->whereBetween('punch_datetime', [
                $from->format('Y-m-d 00:00:00'),
                $to->format('Y-m-d 23:59:59'),
            ]);

        if (! $isFilo) {
            $query->whereIn('status', [
                AttendancePunch::STATUS_PENDING,
                AttendancePunch::STATUS_ERROR,
                AttendancePunch::STATUS_IGNORED,
            ]);
        }

        $punches = $query->orderBy('punch_datetime')->orderBy('id')->get();

        $reset = 0;
        foreach ($punches as $punch) {
            $punch->forceFill([
                'status' => AttendancePunch::STATUS_PENDING,
                'error_message' => null,
                'processed_at' => null,
                'processed_type' => null,
                'is_duplicate' => false,
                'user_id' => $isFilo ? $punch->user_id : null,
                'attendance_id' => null,
                'work_duration_id' => null,
                'attendance_date' => $isFilo ? $punch->attendance_date : null,
            ])->save();
            $reset++;
        }

        if ($isFilo) {
            $days = [];
            foreach ($punches as $punch) {
                $this->ingestService->applyPunch((int) $punch->id);
                $fresh = AttendancePunch::withoutGlobalScopes()->find($punch->id);
                if ($fresh?->user_id && $fresh->attendance_date) {
                    $key = $fresh->user_id.'|'.$fresh->attendance_date->format('Y-m-d');
                    $days[$key] = [
                        'user_id' => (int) $fresh->user_id,
                        'date' => $fresh->attendance_date->format('Y-m-d'),
                    ];
                }
            }
            foreach ($days as $day) {
                $this->filoService->recalculateEmployeeAttendance(
                    (int) $device->company_id,
                    $day['user_id'],
                    $day['date']
                );
            }
        } else {
            foreach ($punches as $punch) {
                $this->ingestService->applyPunch((int) $punch->id);
            }
        }

        $fresh = AttendancePunch::withoutGlobalScopes()
            ->where('company_id', $device->company_id)
            ->where('biometric_device_id', $device->id)
            ->whereBetween('punch_datetime', [
                $from->format('Y-m-d 00:00:00'),
                $to->format('Y-m-d 23:59:59'),
            ])
            ->whereIn('id', $punches->pluck('id'))
            ->get(['status']);

        $summary = [
            'month' => $from->format('Y-m'),
            'reset' => $reset,
            'applied' => $fresh->where('status', AttendancePunch::STATUS_APPLIED)->count(),
            'ignored' => $fresh->where('status', AttendancePunch::STATUS_IGNORED)->count(),
            'error' => $fresh->where('status', AttendancePunch::STATUS_ERROR)->count(),
            'pending' => $fresh->where('status', AttendancePunch::STATUS_PENDING)->count(),
            'total' => $reset,
        ];

        Log::info('zkteco.device_monthly_sync', [
            'device_id' => $device->id,
            'company_id' => $device->company_id,
            'filo' => $isFilo,
            'summary' => $summary,
        ]);

        return $summary;
    }
}
