<?php

namespace Modules\Attendance\Services;

use App\Models\ActivityLogModel;
use App\Models\AttendanceRecordModel;
use App\Models\EmployeeModel;
use App\Models\EmployeeShiftAssignmentModel;
use App\Models\HolidayModel;
use App\Models\LeaveRequestModel;

class AutoPunchService
{
    protected AttendanceRecordModel $records;
    protected EmployeeModel $employees;
    protected EmployeeShiftAssignmentModel $assignments;
    protected AttendanceService $attendance;
    protected HolidayModel $holidays;
    protected LeaveRequestModel $leaveRequests;
    protected ActivityLogModel $activity;

    public function __construct()
    {
        $this->records       = new AttendanceRecordModel();
        $this->employees     = new EmployeeModel();
        $this->assignments   = new EmployeeShiftAssignmentModel();
        $this->attendance    = new AttendanceService();
        $this->holidays      = new HolidayModel();
        $this->leaveRequests = new LeaveRequestModel();
        $this->activity      = new ActivityLogModel();
    }

    /**
     * Process daily auto-punch for all active directory employees.
     * Creates attendance records for employees without punches.
     */
    public function runForDate(string $date, ?int $actingUserId = null): array
    {
        $stats = [
            'date'         => $date,
            'processed'    => 0,
            'auto_present' => 0,
            'absent'       => 0,
            'on_leave'     => 0,
            'holiday'      => 0,
            'skipped'      => 0,
        ];

        $isHoliday = $this->attendance->isHoliday($date);
        $employees = $this->employees->scopeForList(null, 'active')->findAll();

        foreach ($employees as $employee) {
            $employeeId = (int) $employee['id'];
            $stats['processed']++;

            $existing = $this->records->findForEmployeeOnDate($employeeId, $date);

            if ($existing && in_array($existing['source'], ['clock', 'manual', 'correction'], true) && $existing['clock_in']) {
                $stats['skipped']++;
                continue;
            }

            if ($this->attendance->isOnLeave($employeeId, $date)) {
                $this->upsertRecord($employeeId, $date, [
                    'status' => 'on_leave',
                    'source' => 'auto',
                    'notes'  => 'Auto-marked: approved leave',
                ], null, $actingUserId, $existing);
                $stats['on_leave']++;
                continue;
            }

            if ($isHoliday) {
                $this->upsertRecord($employeeId, $date, [
                    'status' => 'holiday',
                    'source' => 'auto',
                    'notes'  => 'Auto-marked: company holiday',
                ], null, $actingUserId, $existing);
                $stats['holiday']++;
                continue;
            }

            $shift = $this->assignments->getShiftForEmployeeOnDate($employeeId, $date);

            if ($shift && ! empty($shift['auto_punch'])) {
                $clockIn  = "{$date} {$shift['start_time']}";
                $clockOut = "{$date} {$shift['end_time']}";

                if (strtotime($clockOut) <= strtotime($clockIn)) {
                    $clockOut = date('Y-m-d H:i:s', strtotime($clockOut . ' +1 day'));
                }

                $data = [
                    'shift_id'  => (int) $shift['shift_id'],
                    'clock_in'  => $clockIn,
                    'clock_out' => $clockOut,
                    'status'    => 'auto_present',
                    'source'    => 'auto',
                    'notes'     => 'Auto-punched from shift schedule',
                ];

                $data = $this->attendance->applyMetrics(array_merge([
                    'employee_id'     => $employeeId,
                    'attendance_date' => $date,
                ], $data), $shift);

                $this->upsertRecord($employeeId, $date, $data, (int) $shift['shift_id'], $actingUserId, $existing);
                $stats['auto_present']++;
            } else {
                $this->upsertRecord($employeeId, $date, [
                    'status' => 'absent',
                    'source' => 'auto',
                    'notes'  => 'Auto-marked: no punch recorded',
                ], $shift ? (int) $shift['shift_id'] : null, $actingUserId, $existing);
                $stats['absent']++;
            }
        }

        $this->activity->log(
            'attendance',
            'auto_punch',
            $actingUserId,
            "Auto punch for {$date}: {$stats['auto_present']} present, {$stats['absent']} absent, {$stats['on_leave']} on leave",
            $date
        );

        return $stats;
    }

    protected function upsertRecord(
        int $employeeId,
        string $date,
        array $data,
        ?int $shiftId,
        ?int $actingUserId,
        ?array $existing
    ): void {
        $payload = array_merge([
            'employee_id'     => $employeeId,
            'attendance_date' => $date,
            'shift_id'        => $shiftId,
            'updated_by'      => $actingUserId,
        ], $data);

        if ($existing) {
            $this->records->update($existing['id'], $payload);
        } else {
            $payload['created_by'] = $actingUserId;
            $this->records->insert($payload);
        }
    }
}
