<?php

namespace Tests\Unit;

use App\Models\Company;
use App\Models\Leave;
use App\Models\LeaveType;
use App\Models\LeaveTypeRule;
use App\Models\User;
use App\Services\Leave\LeaveLedgerService;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class LeaveLedgerServiceTest extends TestCase
{
    use RefreshDatabase;

    public function test_yearly_allocation_and_leave_hold_consume_flow(): void
    {
        $company = Company::query()->create([
            'name' => 'Ledger Co',
            'short_name' => 'LC',
            'email' => 'ledger@co.test',
            'status' => 'active',
        ]);

        $user = User::factory()->create([
            'company_id' => $company->id,
            'user_type' => User::USER_TYPE_STAFF,
        ]);

        $leaveType = LeaveType::query()->create([
            'company_id' => $company->id,
            'name' => 'Casual',
            'short_code' => 'CL',
            'is_paid' => true,
            'total_leaves' => 12,
            'leave_count' => 12,
            'apply_to_all' => true,
        ]);

        LeaveTypeRule::query()->create([
            'company_id' => $company->id,
            'leave_type_id' => $leaveType->id,
            'accrual_mode' => 'yearly',
            'approval_levels' => 1,
        ]);

        $ledger = app(LeaveLedgerService::class);
        $available = $ledger->availableLeaveDays($user, $leaveType);
        $this->assertSame(12.0, $available);

        $leave = Leave::query()->create([
            'company_id' => $company->id,
            'user_id' => $user->id,
            'leave_type_id' => $leaveType->id,
            'start_date' => now()->toDateString(),
            'end_date' => now()->toDateString(),
            'total_days' => 2,
            'reason' => 'Test',
            'status' => 'pending',
        ]);

        $ledger->holdPending($leave);
        $this->assertSame(10.0, $ledger->availableLeaveDays($user, $leaveType));

        $ledger->consumePending($leave);
        $this->assertSame(10.0, $ledger->availableLeaveDays($user, $leaveType));

        $rejectLeave = Leave::query()->create([
            'company_id' => $company->id,
            'user_id' => $user->id,
            'leave_type_id' => $leaveType->id,
            'start_date' => now()->addDay()->toDateString(),
            'end_date' => now()->addDay()->toDateString(),
            'total_days' => 1,
            'reason' => 'Reject me',
            'status' => 'pending',
        ]);
        $ledger->holdPending($rejectLeave);
        $this->assertSame(9.0, $ledger->availableLeaveDays($user, $leaveType));
        $ledger->releasePending($rejectLeave);
        $this->assertSame(10.0, $ledger->availableLeaveDays($user, $leaveType));
    }

    public function test_monthly_accrual_and_carry_forward(): void
    {
        Carbon::setTestNow('2026-06-01 09:00:00');

        $company = Company::query()->create([
            'name' => 'Accrual Co',
            'short_name' => 'AC',
            'email' => 'accrual@co.test',
            'status' => 'active',
        ]);

        $user = User::factory()->create([
            'company_id' => $company->id,
            'user_type' => User::USER_TYPE_STAFF,
            'joining_date' => '2026-01-01',
        ]);

        $leaveType = LeaveType::query()->create([
            'company_id' => $company->id,
            'name' => 'Earned',
            'short_code' => 'EL',
            'is_paid' => true,
            'total_leaves' => 12,
            'leave_count' => 12,
            'apply_to_all' => true,
        ]);

        LeaveTypeRule::query()->create([
            'company_id' => $company->id,
            'leave_type_id' => $leaveType->id,
            'accrual_mode' => 'monthly',
            'pro_rata_on_joining' => true,
            'carry_forward_enabled' => true,
            'carry_forward_limit' => 5,
            'approval_levels' => 1,
        ]);

        $ledger = app(LeaveLedgerService::class);
        $processed = $ledger->runMonthlyAccrual((int) $company->id, Carbon::parse('2026-06-01'));
        $this->assertSame(1, $processed);
        $this->assertSame(1.0, $ledger->availableLeaveDays($user, $leaveType));

        $balance = $ledger->ensureBalance($user, $leaveType, 2026);
        $balance->used = 0;
        $balance->pending = 0;
        $balance->accrued = 3;
        $balance->save();

        $carried = $ledger->runCarryForward((int) $company->id, 2026);
        $this->assertSame(1, $carried);

        $nextYearBalance = $ledger->ensureBalance($user, $leaveType, 2027);
        $this->assertSame(3.0, (float) $nextYearBalance->carried_forward);

        Carbon::setTestNow();
    }
}
