<?php

namespace App\Modules\HR\Attendance\Services;

use App\Modules\HR\Attendance\Models\AttendanceCorrectionRequest;
use App\Modules\HR\Attendance\Models\AttendanceRecord;
use App\Modules\HR\Attendance\Support\AttendanceCorrectionType;
use App\Modules\HR\Employees\Models\Employee;
use Carbon\CarbonImmutable;

class AttendanceCorrectionApplierService
{
    public function __construct(
        private readonly AttendancePunchMetricsService $metrics,
    ) {}

    /**
     * @return array{0: array<string, mixed>, 1: array<string, mixed>}
     */
    public function buildRecordPatchAndMetadata(
        AttendanceCorrectionRequest $correction,
        AttendanceRecord $record,
        int $reviewerUserId,
    ): array {
        $originalIn = $record->check_in_at?->toIso8601String();
        $originalOut = $record->check_out_at?->toIso8601String();
        $patch = $this->recordAttributes($correction, $record, $reviewerUserId);
        $patch = array_merge($patch, $this->metricsForPatchedRecord($record, $patch));
        $meta = array_merge($correction->metadata ?? [], [
            'original_check_in' => $originalIn,
            'original_check_out' => $originalOut,
            'applied_at' => now()->toIso8601String(),
        ]);

        return [$patch, $meta];
    }

    /**
     * @return array<string, mixed>
     */
    private function recordAttributes(
        AttendanceCorrectionRequest $c,
        AttendanceRecord $record,
        int $reviewerUserId,
    ): array {
        $attrs = ['updated_by' => $reviewerUserId];
        $type = $c->type;

        if ($type === AttendanceCorrectionType::MISSING_CHECKOUT) {
            $attrs['check_out_at'] = $c->requested_check_out;
            $attrs['missing_checkout'] = false;

            return $attrs;
        }
        if ($type === AttendanceCorrectionType::WRONG_CHECKIN) {
            $attrs['check_in_at'] = $c->requested_check_in;

            return $attrs;
        }
        if ($type === AttendanceCorrectionType::WRONG_CHECKOUT) {
            $attrs['check_out_at'] = $c->requested_check_out;

            return $attrs;
        }
        if ($c->requested_check_in !== null) {
            $attrs['check_in_at'] = $c->requested_check_in;
        }
        if ($c->requested_check_out !== null) {
            $attrs['check_out_at'] = $c->requested_check_out;
        }
        if (array_key_exists('check_out_at', $attrs)) {
            $attrs['missing_checkout'] = false;
        }

        return $attrs;
    }

    /**
     * @param  array<string, mixed>  $patch
     * @return array<string, mixed>
     */
    private function metricsForPatchedRecord(AttendanceRecord $record, array $patch): array
    {
        $employee = $record->employee ?? Employee::query()->find($record->employee_id);
        if ($employee === null) {
            return [];
        }
        $date = $record->attendance_date?->toDateString();
        if ($date === null) {
            return [];
        }
        $checkIn = $patch['check_in_at'] ?? $record->check_in_at;
        $checkOut = $patch['check_out_at'] ?? $record->check_out_at;
        if ($checkIn === null) {
            return [];
        }
        $in = CarbonImmutable::parse($checkIn);
        if ($checkOut === null) {
            return [
                'status' => $this->metrics->statusAtCheckIn($employee, $date, $in),
                'overtime_minutes' => 0,
            ];
        }
        $out = CarbonImmutable::parse($checkOut);
        $computed = $this->metrics->atCheckOut($employee, $date, $in, $out);

        return [
            'status' => $computed['status'],
            'overtime_minutes' => $computed['overtime_minutes'],
        ];
    }
}
