<?php

namespace Tests\Feature;

use App\Http\Middleware\EnsurePlatformSession;
use App\Models\Bill;
use App\Models\Branch;
use App\Models\Consultation;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\User;
use App\Models\Visit;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class CareBillTest extends TestCase
{
    use RefreshDatabase;

    protected User $user;

    protected User $cashierUser;

    protected Organization $organization;

    protected Visit $visit;

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

        $this->user = User::create([
            'public_id' => 'test-user-001',
            'name' => 'Billing Officer',
            'email' => 'billing@example.com',
        ]);

        $this->cashierUser = User::create([
            'public_id' => 'test-cashier-001',
            'name' => 'Cashier',
            'email' => 'cashier@example.com',
        ]);

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

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

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

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

        $patient = Patient::create([
            'uuid' => (string) \Illuminate\Support\Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $branch->id,
            'patient_number' => 'LC-2026-00001',
            'first_name' => 'Kofi',
            'last_name' => 'Asante',
        ]);

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

        Consultation::create([
            'uuid' => (string) \Illuminate\Support\Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'visit_id' => $this->visit->id,
            'patient_id' => $patient->id,
            'status' => Consultation::STATUS_COMPLETED,
            'started_at' => now()->subHour(),
            'completed_at' => now(),
        ]);
    }

    public function test_billing_officer_can_generate_bill_from_visit(): void
    {
        $this->actingAs($this->user)
            ->post(route('care.bills.generate', $this->visit))
            ->assertRedirect();

        $bill = Bill::first();
        $this->assertNotNull($bill);
        $this->assertSame(Bill::STATUS_OPEN, $bill->status);
        $this->assertTrue($bill->lineItems()->where('type', 'consultation')->exists());
        $this->assertDatabaseHas('care_audit_logs', ['action' => 'bill.created']);
    }

    public function test_cashier_cannot_edit_invoice_lines(): void
    {
        $this->actingAs($this->user)
            ->post(route('care.bills.generate', $this->visit));

        $bill = Bill::firstOrFail();

        $this->actingAs($this->cashierUser)
            ->post(route('care.bills.line-items.store', $bill), [
                'type' => 'misc',
                'description' => 'Dressing supplies',
                'quantity' => 2,
                'unit_price_minor' => 1000,
            ])
            ->assertForbidden();
    }

    public function test_billing_officer_can_add_line_item_and_cashier_records_payments(): void
    {
        $this->actingAs($this->user)
            ->post(route('care.bills.generate', $this->visit));

        $bill = Bill::firstOrFail();
        $consultationFee = (int) config('care.billing.consultation_fee_minor', 5000);

        $this->actingAs($this->user)
            ->post(route('care.bills.line-items.store', $bill), [
                'type' => 'misc',
                'description' => 'Dressing supplies',
                'quantity' => 2,
                'unit_price_minor' => 1000,
            ])
            ->assertRedirect();

        $bill->refresh();
        $expectedTotal = $consultationFee + 2000;

        $this->assertSame($expectedTotal, $bill->total_minor);

        // Billing officer cannot receive payments.
        $this->actingAs($this->user)
            ->post(route('care.bills.payments.store', $bill), [
                'amount' => 30.00,
                'method' => 'cash',
            ])
            ->assertForbidden();

        $this->actingAs($this->cashierUser)
            ->post(route('care.bills.payments.store', $bill), [
                'amount' => 30.00,
                'method' => 'cash',
            ])
            ->assertRedirect();

        $bill->refresh();
        $this->assertSame(Bill::STATUS_PARTIAL, $bill->status);
        $this->assertSame(3000, $bill->amount_paid_minor);

        $this->actingAs($this->cashierUser)
            ->post(route('care.bills.payments.store', $bill), [
                'amount' => $bill->balance_minor / 100,
                'method' => 'momo',
                'reference' => 'MOMO-123',
            ])
            ->assertRedirect();

        $bill->refresh();
        $this->assertSame(Bill::STATUS_PAID, $bill->status);
        $this->assertSame(0, $bill->balance_minor);
        $this->assertDatabaseHas('care_audit_logs', ['action' => 'payment.recorded']);
    }

    public function test_bills_index_is_accessible(): void
    {
        $this->actingAs($this->user)
            ->post(route('care.bills.generate', $this->visit));

        $this->actingAs($this->cashierUser)
            ->get(route('care.bills.index'))
            ->assertOk()
            ->assertSee('INV-');
    }

    public function test_cashier_bills_index_defaults_to_outstanding(): void
    {
        $this->actingAs($this->user)
            ->post(route('care.bills.generate', $this->visit));

        $open = Bill::firstOrFail();

        $paid = Bill::create([
            'uuid' => (string) \Illuminate\Support\Str::uuid(),
            'owner_ref' => $this->user->public_id,
            'organization_id' => $this->organization->id,
            'branch_id' => $this->visit->branch_id,
            'visit_id' => $this->visit->id,
            'patient_id' => $this->visit->patient_id,
            'invoice_number' => 'INV-PAID-001',
            'status' => Bill::STATUS_PAID,
            'subtotal_minor' => 1000,
            'total_minor' => 1000,
            'amount_paid_minor' => 1000,
            'balance_minor' => 0,
        ]);

        $this->actingAs($this->cashierUser)
            ->get(route('care.bills.index'))
            ->assertOk()
            ->assertSee($open->invoice_number)
            ->assertSee('Pay')
            ->assertDontSee($paid->invoice_number, false);

        $this->actingAs($this->cashierUser)
            ->get(route('care.bills.index', ['status' => '']))
            ->assertOk()
            ->assertSee($paid->invoice_number);
    }

    public function test_cashier_bills_index_links_each_invoice_for_payment(): void
    {
        $this->actingAs($this->user)
            ->post(route('care.bills.generate', $this->visit));

        $bill = Bill::firstOrFail();

        $this->actingAs($this->cashierUser)
            ->get(route('care.bills.index'))
            ->assertOk()
            ->assertSee($bill->invoice_number)
            ->assertSee(route('care.bills.show', $bill), false)
            ->assertSee('Pay')
            ->assertSee('Actions');

        $balanceMajor = number_format($bill->balance_minor / 100, 2, '.', '');

        $this->actingAs($this->cashierUser)
            ->get(route('care.bills.show', $bill))
            ->assertOk()
            ->assertSee('Record payment')
            ->assertSee('name="amount"', false)
            ->assertSee('value="'.$balanceMajor.'"', false)
            ->assertDontSee('value="'.$bill->balance_minor.'"', false);
    }

    public function test_call_next_empty_booth_explains_walk_up_invoices(): void
    {
        \Illuminate\Support\Facades\Http::fake();

        $settings = $this->organization->settings ?? [];
        $settings['queue_integration_enabled'] = true;
        $this->organization->update(['settings' => $settings]);

        $this->actingAs($this->cashierUser)
            ->post(route('care.bills.call-next'))
            ->assertRedirect()
            ->assertSessionHas('info');
    }
}
