<?php

namespace App\Modules\HR\PermissionRequests\Services;

use App\Modules\HR\Attendance\Repositories\AttendanceRecordRepository;
use App\Modules\HR\Attendance\Support\AttendanceRecordStatus;
use App\Modules\HR\Attendance\Support\AttendanceSource;
use App\Modules\HR\PermissionRequests\Models\PermissionRequest;
use App\Modules\HR\PermissionRequests\Support\PermissionRequestType;
use App\Modules\Payroll\Services\PayrollProtectionService;

/**
 * Approve-side attendance effects. Soft late/early: notes (+ clear late).
 * Mission/WFH/trip: ensure present without overwriting leave-sourced days.
 * Missing attendance: create present only when no record exists.
 */
class PermissionRequestAttendanceEffectService
{
    public function __construct(
        private readonly AttendanceRecordRepository $records,
        private readonly PayrollProtectionService $payrollProtection,
    ) {}

    public function applyApproved(PermissionRequest $request, int $reviewerId): void
    {
        $date = $request->request_date?->toDateString();
        if ($date === null) {
            return;
        }
        $tenantId = (int) $request->tenant_id;
        $employeeId = (int) $request->employee_id;
        $this->payrollProtection->ensureDateWritable($tenantId, $date, $reviewerId);

        match ($request->type) {
            PermissionRequestType::LATE_ARRIVAL,
            PermissionRequestType::EARLY_LEAVE => $this->applySoftExcuse($tenantId, $employeeId, $date, $request, $reviewerId),
            PermissionRequestType::MISSION,
            PermissionRequestType::WORK_FROM_HOME,
            PermissionRequestType::REMOTE_WORK,
            PermissionRequestType::BUSINESS_TRIP => $this->ensurePresence($tenantId, $employeeId, $date, $request, $reviewerId),
            PermissionRequestType::MANUAL_ATTENDANCE_CORRECTION => null,
            PermissionRequestType::MISSING_ATTENDANCE => $this->fillMissing($tenantId, $employeeId, $date, $request, $reviewerId),
            default => null,
        };
    }

    private function applySoftExcuse(
        int $tenantId,
        int $employeeId,
        string $date,
        PermissionRequest $request,
        int $reviewerId,
    ): void {
        $record = $this->records->findForEmployeeDateInTenant($tenantId, $employeeId, $date);
        if ($record === null) {
            return;
        }
        $payload = [
            'notes' => $this->appendNote($record->notes, $this->excuseNote($request)),
            'updated_by' => $reviewerId,
        ];
        if ($request->type === PermissionRequestType::LATE_ARRIVAL
            && $record->status === AttendanceRecordStatus::LATE) {
            $payload['status'] = AttendanceRecordStatus::PRESENT;
        }
        $this->records->updateOrCreateDay($tenantId, $employeeId, $date, $payload);
    }

    private function ensurePresence(
        int $tenantId,
        int $employeeId,
        string $date,
        PermissionRequest $request,
        int $reviewerId,
    ): void {
        $existing = $this->records->findForEmployeeDateInTenant($tenantId, $employeeId, $date);
        if ($existing !== null && $existing->source === AttendanceSource::LEAVE) {
            return;
        }
        if ($existing === null) {
            $this->records->updateOrCreateDay($tenantId, $employeeId, $date, [
                'source' => AttendanceSource::SYSTEM,
                'status' => AttendanceRecordStatus::PRESENT,
                'notes' => $this->presenceNote($request),
                'created_by' => $reviewerId,
                'updated_by' => $reviewerId,
            ]);

            return;
        }
        $this->records->updateOrCreateDay($tenantId, $employeeId, $date, [
            'status' => AttendanceRecordStatus::PRESENT,
            'notes' => $this->appendNote($existing->notes, $this->presenceNote($request)),
            'updated_by' => $reviewerId,
        ]);
    }

    private function fillMissing(
        int $tenantId,
        int $employeeId,
        string $date,
        PermissionRequest $request,
        int $reviewerId,
    ): void {
        $existing = $this->records->findForEmployeeDateInTenant($tenantId, $employeeId, $date);
        if ($existing !== null) {
            return;
        }
        $this->records->updateOrCreateDay($tenantId, $employeeId, $date, [
            'source' => AttendanceSource::MANUAL,
            'status' => AttendanceRecordStatus::PRESENT,
            'notes' => $this->presenceNote($request),
            'created_by' => $reviewerId,
            'updated_by' => $reviewerId,
        ]);
    }

    private function excuseNote(PermissionRequest $request): string
    {
        return __('permission_requests.attendance_note.excused', [
            'id' => $request->id,
            'type' => __('permission_requests.types.'.$request->type),
        ]);
    }

    private function presenceNote(PermissionRequest $request): string
    {
        return __('permission_requests.attendance_note.presence', [
            'id' => $request->id,
            'type' => __('permission_requests.types.'.$request->type),
        ]);
    }

    private function appendNote(?string $existing, string $addition): string
    {
        $existing = trim((string) $existing);

        return $existing === '' ? $addition : $existing.' '.$addition;
    }
}
