<?php

namespace Tests\Feature;

use App\Http\Middleware\EnsurePlatformSession;
use App\Models\Appointment;
use App\Models\Branch;
use App\Models\CareQueueTicket;
use App\Models\CareServiceQueue;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\Practitioner;
use App\Models\User;
use App\Services\Care\AppointmentService;
use App\Services\Care\CareQueueBridge;
use App\Services\Care\CareQueueContexts;
use App\Services\Care\CareQueueEngine;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class CareNativeQueueTest extends TestCase
{
    use RefreshDatabase;

    protected User $owner;

    protected Organization $organization;

    protected Branch $branch;

    protected Patient $patient;

    protected Practitioner $practitioner;

    protected function setUp(): void
    {
        parent::setUp();
        $this->withoutMiddleware(EnsurePlatformSession::class);

        $this->owner = User::create([
            'public_id' => 'native-queue-owner',
            'name' => 'Owner',
            'email' => 'native-queue@example.com',
        ]);

        $this->organization = Organization::create([
            'owner_ref' => $this->owner->public_id,
            'name' => 'Native Queue Clinic',
            'slug' => 'native-queue-clinic',
            'settings' => [
                'onboarded' => true,
                'facility_type' => 'clinic',
                'plan' => 'pro',
                'plan_expires_at' => now()->addMonth()->toIso8601String(),
                'queue_integration_enabled' => true,
            ],
        ]);

        Member::create([
            'owner_ref' => $this->owner->public_id,
            'organization_id' => $this->organization->id,
            'user_ref' => $this->owner->public_id,
            'role' => 'receptionist',
        ]);

        $this->branch = Branch::create([
            'owner_ref' => $this->owner->public_id,
            'organization_id' => $this->organization->id,
            'name' => 'Main',
            'is_active' => true,
        ]);

        $this->practitioner = Practitioner::create([
            'owner_ref' => $this->owner->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'name' => 'Dr. Native',
            'room' => 'Room 1',
            'is_active' => true,
        ]);

        $this->patient = Patient::create([
            'owner_ref' => $this->owner->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_number' => 'P-N1',
            'first_name' => 'Kwame',
            'last_name' => 'Mensah',
            'gender' => 'male',
            'date_of_birth' => '1988-05-01',
        ]);
    }

    public function test_bridge_enabled_when_integration_on(): void
    {
        $this->assertTrue(app(CareQueueBridge::class)->isEnabled($this->organization));
    }

    public function test_check_in_issues_native_consultation_ticket(): void
    {
        Http::fake();

        $appointment = Appointment::create([
            'owner_ref' => $this->owner->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_id' => $this->patient->id,
            'practitioner_id' => $this->practitioner->id,
            'type' => Appointment::TYPE_SCHEDULED,
            'status' => Appointment::STATUS_SCHEDULED,
            'scheduled_at' => now()->addHour(),
        ]);

        $result = app(AppointmentService::class)->checkIn(
            $appointment->fresh(['organization', 'patient', 'practitioner']),
            $this->owner->public_id,
            $this->owner->public_id,
        );

        $this->assertNotNull($result->queue_ticket_uuid);
        $this->assertSame('waiting', $result->queue_ticket_status);
        $this->assertStringStartsWith('C', (string) $result->queue_ticket_number);
        $this->assertDatabaseHas('care_queue_tickets', [
            'uuid' => $result->queue_ticket_uuid,
            'ticket_number' => $result->queue_ticket_number,
            'status' => CareQueueTicket::STATUS_WAITING,
            'care_entity' => 'appointment',
            'care_entity_id' => $result->id,
        ]);
        $this->assertDatabaseHas('care_service_queues', [
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'context' => CareQueueContexts::CONSULTATION,
        ]);
        Http::assertNothingSent();
    }

    public function test_call_next_serve_and_complete_without_http(): void
    {
        Http::fake();

        $appointment = Appointment::create([
            'owner_ref' => $this->owner->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_id' => $this->patient->id,
            'practitioner_id' => $this->practitioner->id,
            'type' => Appointment::TYPE_WALK_IN,
            'status' => Appointment::STATUS_SCHEDULED,
            'scheduled_at' => now(),
        ]);

        app(AppointmentService::class)->checkIn(
            $appointment->fresh(['organization', 'patient', 'practitioner']),
            $this->owner->public_id,
            $this->owner->public_id,
        );

        $bridge = app(CareQueueBridge::class);
        $called = $bridge->callNext(
            $this->organization->fresh(),
            CareQueueContexts::CONSULTATION,
            (int) $this->branch->id,
            null,
            $this->practitioner->id,
        );

        $this->assertNotNull($called['ticket']);
        $this->assertSame('called', $called['ticket']['status']);
        $this->assertNotNull($called['entity']);
        $this->assertSame('called', $called['entity']->queue_ticket_status);

        $serving = $bridge->serve($this->organization->fresh(), $called['entity']->fresh());
        $this->assertSame('serving', $serving['status'] ?? null);

        $completed = $bridge->complete($this->organization->fresh(), $called['entity']->fresh());
        $this->assertSame('completed', $completed['status'] ?? null);

        $this->assertDatabaseHas('care_queue_tickets', [
            'uuid' => $called['ticket']['uuid'],
            'status' => CareQueueTicket::STATUS_COMPLETED,
        ]);
        Http::assertNothingSent();
    }

    public function test_engine_shared_pool_call_next_claims_unassigned_ticket(): void
    {
        $queue = CareServiceQueue::create([
            'owner_ref' => $this->owner->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'context' => CareQueueContexts::PHARMACY,
            'name' => 'Pharmacy',
            'prefix' => 'P',
            'routing_mode' => 'shared_pool',
            'external_key' => CareQueueContexts::queueExternalKey(CareQueueContexts::PHARMACY, $this->branch->id),
        ]);

        $point = $queue->points()->create([
            'owner_ref' => $this->owner->public_id,
            'organization_id' => $this->organization->id,
            'name' => 'Pharmacy counter',
            'destination' => 'Pharmacy counter',
            'kind' => 'default',
            'external_key' => CareQueueContexts::pointExternalKey(
                CareQueueContexts::PHARMACY,
                $this->branch->id,
                'default',
                0,
            ),
        ]);

        $engine = app(CareQueueEngine::class);
        $issued = $engine->issueTicket($this->organization, [
            'service_queue_id' => $queue->uuid,
            'customer_name' => 'Kwame Mensah',
            'priority' => 'walk_in',
            'metadata' => ['care_entity' => 'prescription'],
        ]);

        $this->assertSame('waiting', $issued['status']);
        $this->assertNull($issued['assigned_counter']);

        $called = $engine->callNext($this->organization, $queue->uuid, $point->uuid);
        $this->assertNotNull($called);
        $this->assertSame('called', $called['status']);
        $this->assertSame($point->uuid, $called['assigned_counter']['uuid'] ?? null);
    }
}
