<?php

namespace Tests\Feature;

use App\Http\Middleware\EnsurePlatformSession;
use App\Models\Branch;
use App\Models\Consultation;
use App\Models\Device;
use App\Models\InvestigationRequest;
use App\Models\InvestigationType;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\User;
use App\Models\Visit;
use App\Models\VitalSign;
use App\Services\Care\DeviceService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Tests\TestCase;

class CareDeviceTest extends TestCase
{
    use RefreshDatabase;

    protected User $user;

    protected Organization $organization;

    protected Branch $branch;

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

        $this->user = User::create([
            'public_id' => 'device-admin-001',
            'name' => 'Device Admin',
            'email' => 'devices@example.com',
        ]);

        $this->organization = Organization::create([
            'owner_ref' => $this->user->public_id,
            'name' => 'Device Clinic',
            'slug' => 'device-clinic',
            'timezone' => 'UTC',
            'settings' => [
                'onboarded' => true,
                'plan' => 'pro',
                'plan_expires_at' => now()->addYear()->toIso8601String(),
            ],
        ]);

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

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

    public function test_admin_can_register_browser_wedge_scanner(): void
    {
        $this->actingAs($this->user)
            ->post(route('care.devices.store'), [
                'name' => 'Front desk scanner',
                'type' => 'barcode_scanner',
                'connection_mode' => 'browser_wedge',
                'branch_id' => $this->branch->id,
            ])
            ->assertRedirect();

        $this->assertDatabaseHas('care_devices', [
            'name' => 'Front desk scanner',
            'type' => 'barcode_scanner',
            'connection_mode' => 'browser_wedge',
            'organization_id' => $this->organization->id,
        ]);
    }

    public function test_free_plan_cannot_register_agent_thermometer(): void
    {
        $this->organization->update([
            'settings' => ['onboarded' => true, 'plan' => 'free'],
        ]);

        $this->actingAs($this->user)
            ->from(route('care.devices.create'))
            ->post(route('care.devices.store'), [
                'name' => 'Ward thermometer',
                'type' => 'thermometer',
                'connection_mode' => 'agent',
                'branch_id' => $this->branch->id,
            ])
            ->assertRedirect(route('care.devices.create'))
            ->assertSessionHas('error');

        $this->assertDatabaseMissing('care_devices', ['name' => 'Ward thermometer']);
    }

    public function test_device_token_heartbeat_and_vitals_provenance(): void
    {
        $devices = app(DeviceService::class);
        $device = Device::create([
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'name' => 'Agent thermometer',
            'type' => 'thermometer',
            'connection_mode' => Device::MODE_AGENT,
            'status' => Device::STATUS_OFFLINE,
        ]);
        $token = $devices->issueToken($device);

        $patient = Patient::create([
            'uuid' => (string) Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_number' => 'LC-DEV-0001',
            'first_name' => 'Ama',
            'last_name' => 'Mensah',
        ]);

        $visit = Visit::create([
            'uuid' => (string) Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_id' => $patient->id,
            'status' => Visit::STATUS_IN_PROGRESS,
            'checked_in_at' => now(),
        ]);

        $consultation = Consultation::create([
            'uuid' => (string) Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'visit_id' => $visit->id,
            'patient_id' => $patient->id,
            'status' => Consultation::STATUS_DRAFT,
            'started_at' => now(),
        ]);

        $this->postJson(route('api.device-agent.heartbeat'), [
            'agent_version' => '0.1.0',
            'hostname' => 'test-host',
        ], [
            'X-Care-Device-Token' => $token,
        ])
            ->assertOk()
            ->assertJsonPath('status', 'active');

        $device->refresh();
        $this->assertSame(Device::STATUS_ACTIVE, $device->status);
        $this->assertNotNull($device->last_seen_at);

        $this->postJson(route('api.device-agent.vitals'), [
            'consultation_uuid' => $consultation->uuid,
            'vitals' => [
                'temperature' => 37.1,
                'pulse' => 78,
            ],
            'raw' => ['driver' => 'test'],
        ], [
            'X-Care-Device-Token' => $token,
        ])
            ->assertCreated()
            ->assertJsonPath('source', 'device');

        $vital = VitalSign::first();
        $this->assertNotNull($vital);
        $this->assertSame(VitalSign::SOURCE_DEVICE, $vital->source);
        $this->assertSame($device->id, $vital->device_id);
        $this->assertSame('37.1', (string) $vital->temperature);
        $this->assertSame('test', $vital->raw_payload['driver'] ?? null);
    }

    public function test_invalid_device_token_is_rejected(): void
    {
        $this->postJson(route('api.device-agent.heartbeat'), [], [
            'X-Care-Device-Token' => 'not-a-real-token',
        ])->assertUnauthorized();
    }

    public function test_patient_scan_lookup_exact_patient_number(): void
    {
        $patient = Patient::create([
            'uuid' => (string) Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_number' => 'LC-SCAN-9999',
            'first_name' => 'Kofi',
            'last_name' => 'Asante',
        ]);

        $this->actingAs($this->user)
            ->getJson(route('care.patients.scan', ['code' => 'LC-SCAN-9999']))
            ->assertOk()
            ->assertJsonPath('patient.patient_number', 'LC-SCAN-9999')
            ->assertJsonPath('patient.uuid', $patient->uuid);

        $this->actingAs($this->user)
            ->getJson(route('care.patients.scan', ['code' => 'UNKNOWN-CODE']))
            ->assertNotFound();
    }

    public function test_agent_can_collect_sample_barcode(): void
    {
        $devices = app(DeviceService::class);
        $device = Device::create([
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'name' => 'Lab scanner agent',
            'type' => 'barcode_scanner',
            'connection_mode' => Device::MODE_AGENT,
            'status' => Device::STATUS_OFFLINE,
        ]);
        $token = $devices->issueToken($device);

        $patient = Patient::create([
            'uuid' => (string) Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_number' => 'LC-LAB-0001',
            'first_name' => 'Efua',
            'last_name' => 'Boateng',
        ]);

        $visit = Visit::create([
            'uuid' => (string) Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_id' => $patient->id,
            'status' => Visit::STATUS_OPEN,
            'checked_in_at' => now(),
        ]);

        $type = InvestigationType::create([
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'name' => 'FBC',
            'code' => 'FBC',
            'category' => 'blood',
            'is_active' => true,
        ]);

        $investigation = InvestigationRequest::create([
            'uuid' => (string) Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'visit_id' => $visit->id,
            'patient_id' => $patient->id,
            'investigation_type_id' => $type->id,
            'status' => InvestigationRequest::STATUS_PENDING,
            'priority' => 'routine',
        ]);

        $this->postJson(route('api.device-agent.samples.collect'), [
            'investigation_uuid' => $investigation->uuid,
            'sample_barcode' => 'SMP-AGENT-1',
        ], [
            'Authorization' => 'Bearer '.$token,
        ])
            ->assertOk()
            ->assertJsonPath('sample_barcode', 'SMP-AGENT-1')
            ->assertJsonPath('status', InvestigationRequest::STATUS_SAMPLE_COLLECTED);
    }

    public function test_patient_label_prints(): void
    {
        $patient = Patient::create([
            'uuid' => (string) Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->branch->id,
            'patient_number' => 'LC-LBL-0001',
            'first_name' => 'Yaw',
            'last_name' => 'Owusu',
        ]);

        $this->actingAs($this->user)
            ->get(route('care.patients.label', $patient))
            ->assertOk()
            ->assertSee('LC-LBL-0001')
            ->assertSee('Yaw Owusu');
    }
}
