<?php

namespace App\Support;

use App\Models\AttendanceDevice;
use App\Models\AttendanceEnrollment;
use App\Models\AttendancePunch;
use App\Models\PermanentEmployee;
use App\Models\PermanentEmployeeSalaryPayment;
use App\Models\TemporaryEmployee;
use Carbon\Carbon;
use Carbon\CarbonInterface;

class StaffAttendance
{
    /**
     * @return array{staff_type: string, staff_id: int, staff_name: string, national_card_number: string|null}|null
     */
    public static function resolveStaff(string $staffType, int $staffId): ?array
    {
        $employee = self::findStaff($staffType, $staffId);

        if (! $employee) {
            return null;
        }

        return [
            'staff_type' => $staffType,
            'staff_id' => $staffId,
            'staff_name' => $employee->full_name,
            'national_card_number' => $employee->national_card_number,
        ];
    }

    public static function findStaff(string $staffType, int $staffId): PermanentEmployee|TemporaryEmployee|null
    {
        return match ($staffType) {
            StaffType::PERMANENT => PermanentEmployee::query()->find($staffId),
            StaffType::TEMPORARY => TemporaryEmployee::query()->find($staffId),
            default => null,
        };
    }

    public static function recordPunch(
        AttendanceDevice $device,
        AttendanceEnrollment $enrollment,
        ?CarbonInterface $punchedAt = null,
        string $punchType = AttendancePunchType::CHECK_IN,
        string $source = AttendanceSource::DEVICE,
    ): AttendancePunch {
        $punch = AttendancePunch::query()->create([
            'attendance_device_id' => $device->id,
            'attendance_enrollment_id' => $enrollment->id,
            'staff_type' => $enrollment->staff_type,
            'staff_id' => $enrollment->staff_id,
            'punched_at' => $punchedAt !== null ? Carbon::parse($punchedAt) : now(),
            'punch_type' => $punchType,
            'source' => $source,
            'device_user_id' => $enrollment->device_user_id,
        ]);

        $device->forceFill(['last_sync_at' => now()])->save();

        PermanentEmployeeSalaryPayment::refreshDraftsForAttendance(
            $enrollment->staff_type,
            $enrollment->staff_id,
            $punch->punched_at,
        );

        return $punch;
    }

    /**
     * @return array{punch: AttendancePunch, enrollment: AttendanceEnrollment, device: AttendanceDevice}|null
     */
    public static function recordDevicePunch(
        string $deviceSerial,
        string $deviceUserId,
        ?string $punchedAt = null,
        ?string $punchType = null,
    ): ?array {
        $device = AttendanceDevice::query()
            ->where('serial_number', $deviceSerial)
            ->where('is_active', true)
            ->first();

        if (! $device) {
            return null;
        }

        $enrollment = AttendanceEnrollment::query()
            ->where('attendance_device_id', $device->id)
            ->where('device_user_id', $deviceUserId)
            ->first();

        if (! $enrollment) {
            return null;
        }

        $at = $punchedAt ? Carbon::parse($punchedAt) : now();

        $punch = self::recordPunch(
            $device,
            $enrollment,
            $at,
            AttendancePunchType::normalize($punchType),
            AttendanceSource::DEVICE,
        );

        return [
            'punch' => $punch,
            'enrollment' => $enrollment,
            'device' => $device,
        ];
    }

    /**
     * @param  iterable<int, array{staff_type: string, staff_id: int}>  $staffKeys
     * @return array<string, array{staff_type: string, staff_id: int, staff_name: string, national_card_number: string|null}>
     */
    public static function staffDirectory(iterable $staffKeys): array
    {
        $permanentIds = [];
        $temporaryIds = [];

        foreach ($staffKeys as $key) {
            if ($key['staff_type'] === StaffType::PERMANENT) {
                $permanentIds[] = $key['staff_id'];
            } elseif ($key['staff_type'] === StaffType::TEMPORARY) {
                $temporaryIds[] = $key['staff_id'];
            }
        }

        $directory = [];

        if ($permanentIds !== []) {
            foreach (PermanentEmployee::query()->whereIn('id', array_unique($permanentIds))->get(['id', 'full_name', 'national_card_number']) as $row) {
                $directory[self::staffKey(StaffType::PERMANENT, $row->id)] = [
                    'staff_type' => StaffType::PERMANENT,
                    'staff_id' => $row->id,
                    'staff_name' => $row->full_name,
                    'national_card_number' => $row->national_card_number,
                ];
            }
        }

        if ($temporaryIds !== []) {
            foreach (TemporaryEmployee::query()->whereIn('id', array_unique($temporaryIds))->get(['id', 'full_name', 'national_card_number']) as $row) {
                $directory[self::staffKey(StaffType::TEMPORARY, $row->id)] = [
                    'staff_type' => StaffType::TEMPORARY,
                    'staff_id' => $row->id,
                    'staff_name' => $row->full_name,
                    'national_card_number' => $row->national_card_number,
                ];
            }
        }

        return $directory;
    }

    public static function staffKey(string $staffType, int $staffId): string
    {
        return "{$staffType}:{$staffId}";
    }
}
