<?php

namespace App\Modules\HR\Shifts\Repositories;

use App\Modules\HR\Shifts\Models\EmployeeShiftAssignment;
use Carbon\CarbonImmutable;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection;

class EmployeeShiftAssignmentRepository
{
    public function coveringInstant(int $tenantId, int $employeeId, CarbonImmutable $instant): ?EmployeeShiftAssignment
    {
        return EmployeeShiftAssignment::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->where('starts_at', '<=', $instant)
            ->where(function ($q) use ($instant): void {
                $q->whereNull('ends_at')->orWhere('ends_at', '>=', $instant);
            })
            ->with('shift')
            ->orderByDesc('starts_at')
            ->first();
    }

    public function coveringDate(int $tenantId, int $employeeId, CarbonImmutable $day): ?EmployeeShiftAssignment
    {
        $start = $day->startOfDay();
        $end = $day->endOfDay();

        return EmployeeShiftAssignment::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->where('starts_at', '<=', $end)
            ->where(function ($q) use ($start): void {
                $q->whereNull('ends_at')->orWhere('ends_at', '>=', $start);
            })
            ->with('shift')
            ->orderByDesc('starts_at')
            ->first();
    }

    /**
     * @return Collection<int, EmployeeShiftAssignment>
     */
    public function activeRowsForEmployee(int $tenantId, int $employeeId): Collection
    {
        return EmployeeShiftAssignment::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->where('is_active', true)
            ->get();
    }

    /**
     * Open timeline rows (no end), including future-dated scheduled assignments.
     *
     * @return Collection<int, EmployeeShiftAssignment>
     */
    public function openRowsForEmployee(int $tenantId, int $employeeId): Collection
    {
        return EmployeeShiftAssignment::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->whereNull('ends_at')
            ->lockForUpdate()
            ->get();
    }

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

    public function closeAssignment(EmployeeShiftAssignment $row, CarbonImmutable $endsAt): void
    {
        $row->forceFill([
            'ends_at' => $endsAt,
            'is_active' => false,
        ])->save();
    }

    public function paginateForTenant(int $tenantId, int $perPage = 30): LengthAwarePaginator
    {
        return EmployeeShiftAssignment::query()
            ->where('tenant_id', $tenantId)
            ->with(['employee:id,full_name,employee_code', 'shift:id,name'])
            ->orderByDesc('starts_at')
            ->orderByDesc('id')
            ->paginate($perPage);
    }

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

    public function calendarForTenant(int $tenantId, CarbonImmutable $month): Collection
    {
        return EmployeeShiftAssignment::query()
            ->where('tenant_id', $tenantId)
            ->where('starts_at', '<=', $month->endOfMonth())
            ->where(function ($query) use ($month): void {
                $query->whereNull('ends_at')->orWhere('ends_at', '>=', $month->startOfMonth());
            })
            ->with(['employee:id,full_name,employee_code', 'shift:id,name,type'])
            ->orderBy('employee_id')
            ->orderBy('starts_at')
            ->get();
    }
}
