<?php

namespace Tests\Unit\Payroll;

use App\Models\User;
use App\Models\WeekoffRule;
use App\Services\Attendance\WeekoffResolver;
use App\Services\Payroll\LeaveLopCalculator;
use Carbon\Carbon;
use Mockery;
use Tests\TestCase;

class LeaveLopCalculatorTest extends TestCase
{
    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }

    protected function calculatorWithWeekendWeekoffs(): LeaveLopCalculator
    {
        $weekoffResolver = Mockery::mock(WeekoffResolver::class);
        $weekoffResolver->shouldReceive('dayTypeMap')
            ->andReturnUsing(function ($companyId, $userId, Carbon $start, Carbon $end) {
                $map = [];
                $cursor = $start->copy()->startOfDay();
                $endDay = $end->copy()->startOfDay();
                while ($cursor->lte($endDay)) {
                    if ($cursor->isWeekend()) {
                        $map[$cursor->format('Y-m-d')] = WeekoffRule::DAY_WEEKLY_OFF;
                    }
                    $cursor->addDay();
                }

                return $map;
            });

        return new LeaveLopCalculator($weekoffResolver);
    }
    public function test_lop_amount_handles_zero_working_days(): void
    {
        $calculator = new LeaveLopCalculator();

        $this->assertSame(0.0, $calculator->lopAmount(50000, 0, 2));
    }

    public function test_working_days_excludes_weekends_and_holidays_without_employee_context(): void
    {
        $calculator = new LeaveLopCalculator();
        $start = Carbon::create(2026, 3, 2)->startOfDay(); // Monday
        $end = Carbon::create(2026, 3, 8)->endOfDay(); // Sunday

        $workingDays = $calculator->workingDaysInPeriod($start, $end, ['2026-03-05']);

        // Weekdays in this range: Mon-Fri = 5, one holiday => 4.
        $this->assertSame(4, $workingDays);
    }

    public function test_joining_date_prorates_payable_working_days(): void
    {
        $calculator = $this->calculatorWithWeekendWeekoffs();
        $user = new User([
            'joining_date' => '2026-03-16',
        ]);
        $user->id = 1;

        $start = Carbon::create(2026, 3, 1)->startOfDay();
        $end = Carbon::create(2026, 3, 31)->endOfDay();

        $context = $calculator->buildPayrollContext($user, 1, $start, $end, []);

        $this->assertTrue($context['eligible']);
        $this->assertSame('2026-03-16', $context['eligible_start']->toDateString());
        $this->assertSame('2026-03-31', $context['eligible_end']->toDateString());
        $this->assertGreaterThan(0, $context['payable_working_days']);
        $this->assertLessThan($context['full_month_working_days'], $context['payable_working_days']);
        $this->assertLessThan(1.0, $context['proration_factor']);
    }

    public function test_last_working_date_before_period_makes_employee_ineligible(): void
    {
        $calculator = new LeaveLopCalculator();
        $user = new User([
            'joining_date' => '2025-01-01',
            'last_working_date' => '2026-02-28',
        ]);

        $start = Carbon::create(2026, 3, 1)->startOfDay();
        $end = Carbon::create(2026, 3, 31)->endOfDay();

        $context = $calculator->buildPayrollContext($user, 1, $start, $end, []);

        $this->assertFalse($context['eligible']);
        $this->assertSame(0.0, $context['proration_factor']);
    }
}

