<?php

namespace Tests\Unit;

use App\Models\Company;
use App\Models\Leave;
use App\Models\LeaveType;
use App\Models\User;
use App\Services\Leave\LeaveRuleValidator;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Validation\ValidationException;
use Tests\TestCase;

class LeaveRuleValidatorOverlapTest extends TestCase
{
    use RefreshDatabase;

    /**
     * @return array{0: User, 1: LeaveType}
     */
    private function makeUserAndLeaveType(): array
    {
        $company = Company::query()->create([
            'name' => 'Overlap Co',
            'short_name' => 'OC',
            'email' => 'overlap@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,
        ]);

        return [$user, $leaveType];
    }

    private function createLeave(User $user, LeaveType $leaveType, string $start, string $end, string $status): Leave
    {
        return Leave::query()->create([
            'company_id' => $user->company_id,
            'user_id' => $user->id,
            'leave_type_id' => $leaveType->id,
            'start_date' => $start,
            'end_date' => $end,
            'total_days' => 1,
            'reason' => 'Test',
            'status' => $status,
        ]);
    }

    public function test_blocks_overlap_with_pending_leave(): void
    {
        [$user, $leaveType] = $this->makeUserAndLeaveType();
        $this->createLeave($user, $leaveType, '2026-09-05', '2026-09-05', 'pending');

        $this->expectException(ValidationException::class);

        app(LeaveRuleValidator::class)->assertNoOverlappingLeave(
            $user,
            Carbon::parse('2026-09-05'),
            Carbon::parse('2026-09-05'),
        );
    }

    public function test_blocks_overlap_with_approved_leave(): void
    {
        [$user, $leaveType] = $this->makeUserAndLeaveType();
        $this->createLeave($user, $leaveType, '2026-09-05', '2026-09-05', 'approved');

        try {
            app(LeaveRuleValidator::class)->assertNoOverlappingLeave(
                $user,
                Carbon::parse('2026-09-05'),
                Carbon::parse('2026-09-05'),
            );
            $this->fail('Expected ValidationException was not thrown.');
        } catch (ValidationException $e) {
            $this->assertArrayHasKey('start_date', $e->errors());
        }
    }

    public function test_allows_overlap_when_existing_is_rejected(): void
    {
        [$user, $leaveType] = $this->makeUserAndLeaveType();
        $this->createLeave($user, $leaveType, '2026-09-05', '2026-09-05', 'rejected');

        app(LeaveRuleValidator::class)->assertNoOverlappingLeave(
            $user,
            Carbon::parse('2026-09-05'),
            Carbon::parse('2026-09-05'),
        );

        $this->assertTrue(true);
    }

    public function test_allows_non_overlapping_dates(): void
    {
        [$user, $leaveType] = $this->makeUserAndLeaveType();
        $this->createLeave($user, $leaveType, '2026-09-05', '2026-09-05', 'approved');

        app(LeaveRuleValidator::class)->assertNoOverlappingLeave(
            $user,
            Carbon::parse('2026-09-06'),
            Carbon::parse('2026-09-06'),
        );

        $this->assertTrue(true);
    }

    public function test_blocks_partial_range_overlap(): void
    {
        [$user, $leaveType] = $this->makeUserAndLeaveType();
        $this->createLeave($user, $leaveType, '2026-09-04', '2026-09-06', 'pending');

        $this->expectException(ValidationException::class);

        app(LeaveRuleValidator::class)->assertNoOverlappingLeave(
            $user,
            Carbon::parse('2026-09-05'),
            Carbon::parse('2026-09-05'),
        );
    }

    public function test_ignores_own_leave_id_on_update(): void
    {
        [$user, $leaveType] = $this->makeUserAndLeaveType();
        $leave = $this->createLeave($user, $leaveType, '2026-09-05', '2026-09-05', 'pending');

        app(LeaveRuleValidator::class)->assertNoOverlappingLeave(
            $user,
            Carbon::parse('2026-09-05'),
            Carbon::parse('2026-09-05'),
            $leave->id,
        );

        $this->assertTrue(true);
    }
}
