<?php

namespace App\Modules\HR\Overtime\Repositories;

use App\Modules\HR\Overtime\Models\OvertimeRequest;
use App\Modules\HR\Overtime\Support\OvertimeRequestStatus;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

class OvertimeRequestRepository
{
    /**
     * @param  array<string, mixed>  $data
     */
    public function create(array $data): OvertimeRequest
    {
        return OvertimeRequest::query()->create($data);
    }

    public function findForTenant(int $tenantId, int $id): ?OvertimeRequest
    {
        return OvertimeRequest::query()
            ->where('tenant_id', $tenantId)
            ->whereKey($id)
            ->first();
    }

    public function lockForTenant(int $tenantId, int $id): ?OvertimeRequest
    {
        return OvertimeRequest::query()
            ->where('tenant_id', $tenantId)
            ->whereKey($id)
            ->lockForUpdate()
            ->first();
    }

    public function paginateForTenant(int $tenantId, int $perPage = 20): LengthAwarePaginator
    {
        return OvertimeRequest::query()
            ->where('tenant_id', $tenantId)
            ->with(['employee'])
            ->orderByDesc('attendance_date')
            ->paginate($perPage);
    }

    public function paginateForEmployee(int $tenantId, int $employeeId, int $perPage = 20): LengthAwarePaginator
    {
        return OvertimeRequest::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->orderByDesc('attendance_date')
            ->paginate($perPage);
    }

    public function existsBlockingForEmployeeDate(int $tenantId, int $employeeId, string $date): bool
    {
        return OvertimeRequest::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->whereDate('attendance_date', $date)
            ->whereIn('status', OvertimeRequestStatus::blocking())
            ->exists();
    }

    public function pendingCountForTenant(int $tenantId): int
    {
        return OvertimeRequest::query()
            ->where('tenant_id', $tenantId)
            ->where('status', OvertimeRequestStatus::PENDING)
            ->count();
    }

    /**
     * Sum of approved overtime minutes. Returns null when no approved rows exist
     * so payroll can fall back to attendance overtime_minutes storage.
     */
    public function approvedOvertimeMinutesForEmployeeInRange(
        int $tenantId,
        int $employeeId,
        string $from,
        string $to,
    ): ?int {
        $base = OvertimeRequest::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->where('status', OvertimeRequestStatus::APPROVED)
            ->whereBetween('attendance_date', [$from, $to]);

        if (! (clone $base)->exists()) {
            return null;
        }

        return (int) $base->sum('overtime_minutes');
    }

    public function approvedWeightedMinutesForEmployeeInRange(
        int $tenantId,
        int $employeeId,
        string $from,
        string $to,
    ): ?int {
        $rows = OvertimeRequest::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->where('status', OvertimeRequestStatus::APPROVED)
            ->whereBetween('attendance_date', [$from, $to])
            ->get(['overtime_minutes', 'applied_multiplier']);
        if ($rows->isEmpty()) {
            return null;
        }
        $fallback = (float) config('overtime.multiplier', 1);

        return (int) round($rows->sum(
            fn (OvertimeRequest $row) => $row->overtime_minutes * (float) ($row->applied_multiplier ?? $fallback),
        ));
    }

    /**
     * @param  array<string, mixed>  $data
     */
    public function update(OvertimeRequest $row, array $data): OvertimeRequest
    {
        $row->update($data);

        return $row->fresh(['employee']);
    }
}
