<?php

use App\Enums\ConsultationStatus;
use App\Models\AdminLog;
use App\Models\BlockedTime;
use App\Models\Consultation;
use App\Models\User;
use Livewire\Volt\Volt;

beforeEach(function () {
    $this->admin = User::factory()->admin()->create();
});

// ===========================================
// Access Tests
// ===========================================

test('admin can access blocked times page', function () {
    $this->actingAs($this->admin)
        ->get(route('admin.settings.blocked-times'))
        ->assertOk();
});

test('non-admin cannot access blocked times page', function () {
    $client = User::factory()->individual()->create();

    $this->actingAs($client)
        ->get(route('admin.settings.blocked-times'))
        ->assertForbidden();
});

test('unauthenticated user cannot access blocked times page', function () {
    $this->get(route('admin.settings.blocked-times'))
        ->assertRedirect(route('login'));
});

// ===========================================
// Create All-Day Block Tests
// ===========================================

test('admin can create an all-day block', function () {
    $this->actingAs($this->admin);

    $blockDate = today()->addDays(5)->format('Y-m-d');

    $initialCount = BlockedTime::count();

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', $blockDate)
        ->set('is_all_day', true)
        ->set('reason', 'Holiday')
        ->call('save')
        ->assertHasNoErrors();

    expect(BlockedTime::count())->toBe($initialCount + 1);

    $blocked = BlockedTime::latest()->first();

    expect($blocked)->not->toBeNull()
        ->and($blocked->isAllDay())->toBeTrue()
        ->and($blocked->reason)->toBe('Holiday');
});

// ===========================================
// Create Time-Range Block Tests
// ===========================================

test('admin can create a time-range block', function () {
    $this->actingAs($this->admin);

    $initialCount = BlockedTime::count();

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', today()->addDays(3)->format('Y-m-d'))
        ->set('is_all_day', false)
        ->set('start_time', '09:00')
        ->set('end_time', '12:00')
        ->set('reason', 'Morning meeting')
        ->call('save')
        ->assertHasNoErrors();

    expect(BlockedTime::count())->toBe($initialCount + 1);

    $blocked = BlockedTime::latest()->first();

    expect($blocked)->not->toBeNull()
        ->and($blocked->isAllDay())->toBeFalse()
        ->and($blocked->start_time)->toContain('09:00')
        ->and($blocked->end_time)->toContain('12:00')
        ->and($blocked->reason)->toBe('Morning meeting');
});

test('admin can create block without reason', function () {
    $this->actingAs($this->admin);

    $initialCount = BlockedTime::count();

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', today()->addDays(2)->format('Y-m-d'))
        ->set('is_all_day', true)
        ->set('reason', '')
        ->call('save')
        ->assertHasNoErrors();

    expect(BlockedTime::count())->toBe($initialCount + 1);

    $blocked = BlockedTime::latest()->first();

    expect($blocked)->not->toBeNull()
        ->and($blocked->reason)->toBeNull();
});

// ===========================================
// Edit Blocked Time Tests
// ===========================================

test('admin can edit an existing blocked time', function () {
    $blocked = BlockedTime::factory()->allDay()->create([
        'block_date' => today()->addDays(5),
        'reason' => 'Original reason',
    ]);

    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('openEditModal', $blocked->id)
        ->set('reason', 'Updated reason')
        ->call('save')
        ->assertHasNoErrors();

    $blocked->refresh();

    expect($blocked->reason)->toBe('Updated reason');
});

test('admin can change block from all-day to time-range', function () {
    $blocked = BlockedTime::factory()->allDay()->create([
        'block_date' => today()->addDays(5),
    ]);

    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('openEditModal', $blocked->id)
        ->set('is_all_day', false)
        ->set('start_time', '14:00')
        ->set('end_time', '18:00')
        ->call('save')
        ->assertHasNoErrors();

    $blocked->refresh();

    expect($blocked->isAllDay())->toBeFalse()
        ->and($blocked->start_time)->toContain('14:00')
        ->and($blocked->end_time)->toContain('18:00');
});

test('admin can edit past blocked time date', function () {
    $blocked = BlockedTime::factory()->past()->create();

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->call('openEditModal', $blocked->id)
        ->set('reason', 'Updated past block')
        ->call('save')
        ->assertHasNoErrors();

    $blocked->refresh();

    expect($blocked->reason)->toBe('Updated past block');
});

// ===========================================
// Delete Blocked Time Tests
// ===========================================

test('admin can delete a blocked time', function () {
    $blocked = BlockedTime::factory()->create();
    $blockedId = $blocked->id;

    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('confirmDelete', $blockedId)
        ->call('delete')
        ->assertHasNoErrors();

    expect(BlockedTime::find($blockedId))->toBeNull();
});

// ===========================================
// Validation Tests
// ===========================================

test('cannot create block with end time before start time', function () {
    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', today()->addDays(5)->format('Y-m-d'))
        ->set('is_all_day', false)
        ->set('start_time', '14:00')
        ->set('end_time', '10:00')
        ->call('save')
        ->assertHasErrors(['end_time']);
});

test('cannot create block for past dates', function () {
    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', today()->subDays(5)->format('Y-m-d'))
        ->set('is_all_day', true)
        ->call('save')
        ->assertHasErrors(['block_date']);
});

test('can create block for today', function () {
    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', today()->format('Y-m-d'))
        ->set('is_all_day', true)
        ->call('save')
        ->assertHasNoErrors();

    expect(BlockedTime::where('block_date', today())->exists())->toBeTrue();
});

test('reason field respects 255 character max length', function () {
    $this->actingAs($this->admin);

    $longReason = str_repeat('a', 256);

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', today()->addDays(5)->format('Y-m-d'))
        ->set('is_all_day', true)
        ->set('reason', $longReason)
        ->call('save')
        ->assertHasErrors(['reason']);
});

// ===========================================
// List View Tests
// ===========================================

test('list displays all blocked times sorted by date', function () {
    BlockedTime::factory()->create(['block_date' => today()->addDays(10)]);
    BlockedTime::factory()->create(['block_date' => today()->addDays(2)]);
    BlockedTime::factory()->create(['block_date' => today()->addDays(5)]);

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->set('filter', 'all');

    $blockedTimes = $component->viewData('blockedTimes');

    expect($blockedTimes)->toHaveCount(3);
});

test('filter by upcoming shows only future blocks', function () {
    BlockedTime::factory()->upcoming()->create();
    BlockedTime::factory()->today()->create();
    BlockedTime::factory()->past()->create();

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->set('filter', 'upcoming');

    $blockedTimes = $component->viewData('blockedTimes');

    expect($blockedTimes)->toHaveCount(2)
        ->and($blockedTimes->every(fn ($b) => $b->block_date->gte(today())))->toBeTrue();
});

test('filter by past shows only past blocks', function () {
    BlockedTime::factory()->upcoming()->create();
    BlockedTime::factory()->today()->create();
    BlockedTime::factory()->past()->create();

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->set('filter', 'past');

    $blockedTimes = $component->viewData('blockedTimes');

    expect($blockedTimes)->toHaveCount(1)
        ->and($blockedTimes->every(fn ($b) => $b->block_date->lt(today())))->toBeTrue();
});

test('filter by all shows all blocks', function () {
    BlockedTime::factory()->upcoming()->create();
    BlockedTime::factory()->today()->create();
    BlockedTime::factory()->past()->create();

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->set('filter', 'all');

    $blockedTimes = $component->viewData('blockedTimes');

    expect($blockedTimes)->toHaveCount(3);
});

test('list shows reason if provided', function () {
    BlockedTime::factory()->create([
        'block_date' => today()->addDays(5),
        'reason' => 'Personal vacation',
    ]);

    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->assertSee('Personal vacation');
});

// ===========================================
// Audit Log Tests
// ===========================================

test('audit log is created when blocked time is created', function () {
    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', today()->addDays(5)->format('Y-m-d'))
        ->set('is_all_day', true)
        ->set('reason', 'Test')
        ->call('save');

    $log = AdminLog::where('target_type', 'blocked_time')->first();

    expect($log)->not->toBeNull()
        ->and($log->admin_id)->toBe($this->admin->id)
        ->and($log->action)->toBe('create');
});

test('audit log is created when blocked time is updated', function () {
    $blocked = BlockedTime::factory()->create([
        'block_date' => today()->addDays(5),
    ]);

    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('openEditModal', $blocked->id)
        ->set('reason', 'Updated')
        ->call('save');

    $log = AdminLog::where('target_type', 'blocked_time')
        ->where('action', 'update')
        ->first();

    expect($log)->not->toBeNull()
        ->and($log->target_id)->toBe($blocked->id);
});

test('audit log is created when blocked time is deleted', function () {
    $blocked = BlockedTime::factory()->create();
    $blockedId = $blocked->id;

    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('confirmDelete', $blockedId)
        ->call('delete');

    $log = AdminLog::where('target_type', 'blocked_time')
        ->where('action', 'delete')
        ->first();

    expect($log)->not->toBeNull()
        ->and($log->target_id)->toBe($blockedId);
});

// ===========================================
// Pending Booking Warning Tests
// ===========================================

test('warning displayed when blocking date with pending consultations', function () {
    $client = User::factory()->individual()->create();
    $blockDate = today()->addDays(5);

    Consultation::factory()->create([
        'user_id' => $client->id,
        'booking_date' => $blockDate,
        'booking_time' => '10:00:00',
        'status' => ConsultationStatus::Pending,
    ]);

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', $blockDate->format('Y-m-d'))
        ->set('is_all_day', true)
        ->call('checkPendingBookings');

    expect($component->get('pendingBookings'))->toHaveCount(1)
        ->and($component->get('pendingBookings')[0]['client'])->toBe($client->full_name);
});

test('warning shows pending bookings within time range', function () {
    $client = User::factory()->individual()->create();
    $blockDate = today()->addDays(5);

    // Consultation within blocked range (10:00 is between 09:00 and 12:00)
    Consultation::factory()->create([
        'user_id' => $client->id,
        'booking_date' => $blockDate,
        'booking_time' => '10:00:00',
        'status' => ConsultationStatus::Pending,
    ]);

    // Consultation outside blocked range (15:00 is not between 09:00 and 12:00)
    Consultation::factory()->create([
        'user_id' => $client->id,
        'booking_date' => $blockDate,
        'booking_time' => '15:00:00',
        'status' => ConsultationStatus::Pending,
    ]);

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', $blockDate->format('Y-m-d'))
        ->set('is_all_day', false)
        ->set('start_time', '09:00')
        ->set('end_time', '12:00')
        ->call('checkPendingBookings');

    expect($component->get('pendingBookings'))->toHaveCount(1);
});

// ===========================================
// Modal Tests
// ===========================================

test('create modal opens with default values', function () {
    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal');

    expect($component->get('showModal'))->toBeTrue()
        ->and($component->get('editingId'))->toBeNull()
        ->and($component->get('is_all_day'))->toBeTrue()
        ->and($component->get('block_date'))->toBe(today()->format('Y-m-d'));
});

test('edit modal opens with existing values', function () {
    $blocked = BlockedTime::factory()->timeRange('10:00', '14:00')->create([
        'block_date' => today()->addDays(5),
        'reason' => 'Test reason',
    ]);

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->call('openEditModal', $blocked->id);

    expect($component->get('showModal'))->toBeTrue()
        ->and($component->get('editingId'))->toBe($blocked->id)
        ->and($component->get('is_all_day'))->toBeFalse()
        ->and($component->get('start_time'))->toBe('10:00')
        ->and($component->get('end_time'))->toBe('14:00')
        ->and($component->get('reason'))->toBe('Test reason');
});

test('modal closes and resets on close', function () {
    $blocked = BlockedTime::factory()->create();

    $this->actingAs($this->admin);

    $component = Volt::test('admin.settings.blocked-times')
        ->call('openEditModal', $blocked->id)
        ->call('closeModal');

    expect($component->get('showModal'))->toBeFalse()
        ->and($component->get('editingId'))->toBeNull();
});

// ===========================================
// Multiple Blocks on Same Date Tests
// ===========================================

test('multiple time-range blocks on same date handled correctly', function () {
    $blockDate = today()->addDays(5);

    BlockedTime::factory()->timeRange('09:00', '12:00')->create([
        'block_date' => $blockDate,
    ]);

    $this->actingAs($this->admin);

    Volt::test('admin.settings.blocked-times')
        ->call('openCreateModal')
        ->set('block_date', $blockDate->format('Y-m-d'))
        ->set('is_all_day', false)
        ->set('start_time', '14:00')
        ->set('end_time', '17:00')
        ->call('save')
        ->assertHasNoErrors();

    expect(BlockedTime::where('block_date', $blockDate)->count())->toBe(2);
});
