<?php

use App\Enums\ConsultationStatus;
use App\Models\BlockedTime;
use App\Models\Consultation;
use App\Models\WorkingHour;
use App\Services\AvailabilityService;
use Carbon\Carbon;

beforeEach(function () {
    // Setup default working hours (Sunday-Thursday, 9am-5pm)
    foreach ([0, 1, 2, 3, 4] as $day) {
        WorkingHour::factory()->create([
            'day_of_week' => $day,
            'start_time' => '09:00',
            'end_time' => '17:00',
            'is_active' => true,
        ]);
    }
});

describe('getDateStatus', function () {
    it('returns "past" for yesterday', function () {
        $service = new AvailabilityService;
        $yesterday = Carbon::yesterday();

        expect($service->getDateStatus($yesterday))->toBe('past');
    });

    it('returns "closed" for non-working days (weekends)', function () {
        $service = new AvailabilityService;
        // Friday is day 5, Saturday is day 6 - both are weekends in our setup
        $friday = Carbon::now()->next(Carbon::FRIDAY);

        expect($service->getDateStatus($friday))->toBe('closed');
    });

    it('returns "blocked" for fully blocked date', function () {
        $sunday = Carbon::now()->next(Carbon::SUNDAY);
        BlockedTime::factory()->create([
            'block_date' => $sunday->toDateString(),
            'start_time' => null,
            'end_time' => null,
        ]);

        $service = new AvailabilityService;

        expect($service->getDateStatus($sunday))->toBe('blocked');
    });

    it('returns "full" when all slots are booked', function () {
        $sunday = Carbon::now()->next(Carbon::SUNDAY);
        $slots = ['09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00'];

        foreach ($slots as $slot) {
            Consultation::factory()->approved()->create([
                'booking_date' => $sunday->toDateString(),
                'booking_time' => $slot,
            ]);
        }

        $service = new AvailabilityService;

        expect($service->getDateStatus($sunday))->toBe('full');
    });

    it('returns "partial" when some slots are booked', function () {
        $sunday = Carbon::now()->next(Carbon::SUNDAY);
        Consultation::factory()->approved()->create([
            'booking_date' => $sunday->toDateString(),
            'booking_time' => '10:00',
        ]);

        $service = new AvailabilityService;

        expect($service->getDateStatus($sunday))->toBe('partial');
    });

    it('returns "available" for working day with no bookings', function () {
        $sunday = Carbon::now()->next(Carbon::SUNDAY);
        $service = new AvailabilityService;

        expect($service->getDateStatus($sunday))->toBe('available');
    });
});

describe('getAvailableSlots', function () {
    it('returns empty array for non-working days', function () {
        $service = new AvailabilityService;
        // Friday is day 5 - a non-working day in our setup
        $friday = Carbon::now()->next(Carbon::FRIDAY);

        expect($service->getAvailableSlots($friday))->toBe([]);
    });

    it('excludes booked consultation times', function () {
        $sunday = Carbon::now()->next(Carbon::SUNDAY);
        Consultation::factory()->pending()->create([
            'booking_date' => $sunday->toDateString(),
            'booking_time' => '10:00',
        ]);

        $service = new AvailabilityService;
        $slots = $service->getAvailableSlots($sunday);

        expect($slots)->not->toContain('10:00');
    });

    it('excludes blocked time ranges', function () {
        $sunday = Carbon::now()->next(Carbon::SUNDAY);
        BlockedTime::factory()->timeRange('14:00', '16:00')->create([
            'block_date' => $sunday->toDateString(),
        ]);

        $service = new AvailabilityService;
        $slots = $service->getAvailableSlots($sunday);

        expect($slots)->not->toContain('14:00');
        expect($slots)->not->toContain('15:00');
    });

    it('includes pending and approved consultations as booked', function () {
        $sunday = Carbon::now()->next(Carbon::SUNDAY);

        Consultation::factory()->pending()->create([
            'booking_date' => $sunday->toDateString(),
            'booking_time' => '09:00',
        ]);

        Consultation::factory()->approved()->create([
            'booking_date' => $sunday->toDateString(),
            'booking_time' => '10:00',
        ]);

        // Cancelled should NOT block the slot
        Consultation::factory()->create([
            'booking_date' => $sunday->toDateString(),
            'booking_time' => '11:00',
            'status' => ConsultationStatus::Cancelled,
        ]);

        $service = new AvailabilityService;
        $slots = $service->getAvailableSlots($sunday);

        expect($slots)->not->toContain('09:00');
        expect($slots)->not->toContain('10:00');
        expect($slots)->toContain('11:00');
    });

    it('returns all slots blocked when full day is blocked', function () {
        $sunday = Carbon::now()->next(Carbon::SUNDAY);
        BlockedTime::factory()->allDay()->create([
            'block_date' => $sunday->toDateString(),
        ]);

        $service = new AvailabilityService;
        $slots = $service->getAvailableSlots($sunday);

        expect($slots)->toBe([]);
    });
});

describe('getMonthAvailability', function () {
    it('returns status for every day in the month', function () {
        $service = new AvailabilityService;
        $availability = $service->getMonthAvailability(2025, 1);

        expect($availability)->toHaveCount(31);
    });

    it('handles year rollover correctly (December to January)', function () {
        $service = new AvailabilityService;

        $december = $service->getMonthAvailability(2024, 12);
        $january = $service->getMonthAvailability(2025, 1);

        expect($december)->toHaveKey('2024-12-31');
        expect($january)->toHaveKey('2025-01-01');
    });

    it('returns correct status types', function () {
        $service = new AvailabilityService;
        $availability = $service->getMonthAvailability(now()->year, now()->month);

        $validStatuses = ['past', 'closed', 'blocked', 'full', 'partial', 'available'];

        foreach ($availability as $status) {
            expect($validStatuses)->toContain($status);
        }
    });
});
