<?php

namespace App\Modules\Payroll\Repositories;

use App\Modules\Payroll\Models\PayrollPeriod;
use App\Modules\Payroll\Support\PayrollPeriodStatus;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;

class PayrollPeriodRepository
{
    public function findById(int $id): ?PayrollPeriod
    {
        return PayrollPeriod::query()->whereKey($id)->first();
    }

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

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

    /**
     * @param  array<string, mixed>  $data
     */
    public function update(PayrollPeriod $period, array $data): PayrollPeriod
    {
        PayrollPeriod::query()->whereKey($period->id)->update($data);

        return PayrollPeriod::query()->whereKey($period->id)->firstOrFail();
    }

    public function paginateForTenant(int $tenantId, int $perPage = 15): LengthAwarePaginator
    {
        return PayrollPeriod::query()
            ->where('tenant_id', $tenantId)
            ->with(['creator:id,name', 'finalizer:id,name'])
            ->orderByDesc('start_date')
            ->paginate($perPage)
            ->withQueryString();
    }

    public function hasOverlappingDates(int $tenantId, string $start, string $end, ?int $exceptId = null): bool
    {
        $q = PayrollPeriod::query()
            ->where('tenant_id', $tenantId)
            ->whereRaw('NOT (end_date < ? OR start_date > ?)', [$start, $end]);
        if ($exceptId !== null) {
            $q->where('id', '!=', $exceptId);
        }

        return $q->exists();
    }

    /**
     * Finalized periods overlapping [rangeStart, rangeEnd] (inclusive), per tenant.
     * Uses tenant_id + status index, then date predicate.
     */
    public function tenantHasFinalizedPayrollOverlappingRange(
        int $tenantId,
        string $rangeStart,
        string $rangeEnd,
    ): bool {
        return PayrollPeriod::query()
            ->where('tenant_id', $tenantId)
            ->where('status', PayrollPeriodStatus::FINALIZED)
            ->where('start_date', '<=', $rangeEnd)
            ->where('end_date', '>=', $rangeStart)
            ->exists();
    }

    /**
     * Atomically finalize a period from PROCESSING (row lock). Returns null if not eligible.
     */
    public function finalizeFromProcessingIfEligible(
        int $tenantId,
        int $periodId,
        int $finalizedByUserId,
    ): ?PayrollPeriod {
        return DB::transaction(function () use ($tenantId, $periodId, $finalizedByUserId): ?PayrollPeriod {
            $row = PayrollPeriod::query()
                ->where('tenant_id', $tenantId)
                ->whereKey($periodId)
                ->lockForUpdate()
                ->first();
            if ($row === null) {
                return null;
            }
            if ($row->status !== PayrollPeriodStatus::PROCESSING || $row->isFinalized()) {
                return null;
            }
            $row->update([
                'status' => PayrollPeriodStatus::FINALIZED,
                'finalized_at' => now(),
                'finalized_by' => $finalizedByUserId,
            ]);

            return $row->fresh();
        });
    }
}
