<?php

namespace App\Domain\HR\Services;

use App\Domain\HR\Enums\AdvanceStatus;
use App\Domain\HR\Models\Trainer;
use App\Domain\HR\Models\TrainerAdvance;
use App\Domain\Shared\Exceptions\DomainException;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class TrainerAdvanceService
{
    /**
     * Create a new advance for a trainer.
     *
     * Business rules enforced:
     * - A trainer can only have one active advance at a time.
     * - Amount must be positive.
     * - At least one installment must be specified.
     * - The per-installment amount is rounded UP (ceil) so the trainer always
     *   pays back at least the full principal; any over-deduction in the last
     *   installment is capped by the remaining balance inside PayrollService.
     */
    public function create(
        Trainer $trainer,
        int $amount,
        int $installmentsCount,
        ?string $reason,
        User $actor,
    ): TrainerAdvance {
        // Guard: no concurrent active advance
        $existing = TrainerAdvance::where('trainer_id', $trainer->id)
            ->where('status', AdvanceStatus::Active->value)
            ->exists();

        if ($existing) {
            throw new DomainException('يوجد سلفة نشطة بالفعل — يجب تسديدها أو إلغائها أولاً');
        }

        if ($amount <= 0) {
            throw new DomainException('مبلغ السلفة يجب أن يكون أكبر من صفر');
        }

        if ($installmentsCount < 1) {
            throw new DomainException('عدد الأقساط يجب أن يكون قسط واحد على الأقل');
        }

        // Ceiling division so the sum of installments never falls short of principal
        $installmentAmount = (int) ceil($amount / $installmentsCount);

        return DB::transaction(function () use ($trainer, $amount, $installmentAmount, $installmentsCount, $reason, $actor) {
            return TrainerAdvance::create([
                'academy_id'               => $trainer->academy_id,
                'trainer_id'               => $trainer->id,
                'amount'                   => $amount,
                'remaining_balance'        => $amount,
                'installment_amount'       => $installmentAmount,
                'installments_count'       => $installmentsCount,
                'installments_paid'        => 0,
                'reason'                   => $reason,
                'status'                   => AdvanceStatus::Active,
                'issued_date'              => now()->toDateString(),
                'expected_completion_date' => now()->addMonths($installmentsCount)->toDateString(),
                'approved_by'              => $actor->id,
                'created_by'               => $actor->id,
            ]);
        });
    }

    /**
     * Cancel an advance (active or paused advances may be cancelled).
     */
    public function cancel(TrainerAdvance $advance, User $actor): TrainerAdvance
    {
        if (
            $advance->status !== AdvanceStatus::Active &&
            $advance->status !== AdvanceStatus::Paused
        ) {
            throw new DomainException('لا يمكن إلغاء هذه السلفة في حالتها الحالية');
        }

        return DB::transaction(function () use ($advance) {
            $advance->update(['status' => AdvanceStatus::Cancelled]);
            return $advance->fresh();
        });
    }

    /**
     * Pause an advance — stops deductions until explicitly resumed.
     */
    public function pause(TrainerAdvance $advance): TrainerAdvance
    {
        if ($advance->status !== AdvanceStatus::Active) {
            throw new DomainException('لا يمكن إيقاف سلفة غير نشطة');
        }

        return DB::transaction(function () use ($advance) {
            $advance->update(['status' => AdvanceStatus::Paused]);
            return $advance->fresh();
        });
    }

    /**
     * Resume a paused advance — deductions restart on the next payroll cycle.
     */
    public function resume(TrainerAdvance $advance): TrainerAdvance
    {
        if ($advance->status !== AdvanceStatus::Paused) {
            throw new DomainException('هذه السلفة ليست متوقفة');
        }

        return DB::transaction(function () use ($advance) {
            $advance->update(['status' => AdvanceStatus::Active]);
            return $advance->fresh();
        });
    }
}
