<?php

namespace Tests\Feature;

use App\Domain\Billing\InvoiceService;
use App\Domain\Clinical\TreatmentPlanService;
use App\Enums\InvoiceStatus;
use App\Models\Procedure;
use App\Models\Tenant;
use App\Models\TreatmentPlan;
use App\Support\Tenancy;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class InvoiceTest extends TestCase
{
    use RefreshDatabase;

    protected Tenant $tenant;
    protected TreatmentPlan $plan;
    protected Procedure $procedure;

    protected function setUp(): void
    {
        parent::setUp();
        $this->seed();

        $this->tenant = Tenant::create(['name' => 'Smile', 'slug' => 'smile']);
        app(Tenancy::class)->set($this->tenant);

        $this->procedure = Procedure::factory()->create(); // 35,000
        $this->plan = TreatmentPlan::factory()->create(['discount_amount' => 5000]);

        $svc = app(TreatmentPlanService::class);
        $svc->addItem($this->plan, ['procedure_id' => $this->procedure->id, 'tooth' => 16]);
        $b = $svc->addItem($this->plan, ['procedure_id' => $this->procedure->id, 'tooth' => 26, 'price' => 40000]);
        $c = $svc->addItem($this->plan, ['procedure_id' => $this->procedure->id, 'tooth' => 36]);
        $svc->cancelItem($c); // cancelled items must not be billed
    }

    public function test_invoice_from_plan_copies_items_and_discount(): void
    {
        $invoice = app(InvoiceService::class)->createFromTreatmentPlan($this->plan->fresh('items'));

        $this->assertSame(2, $invoice->items()->count());
        $this->assertSame(75000, $invoice->subtotal);
        $this->assertSame(5000, $invoice->discount_amount);
        $this->assertSame(70000, $invoice->total);
        $this->assertSame(InvoiceStatus::Draft, $invoice->status);
        $this->assertMatchesRegularExpression('/^INV-\d{4}-0001$/', $invoice->number);
    }

    public function test_numbering_increments_per_tenant(): void
    {
        $svc = app(InvoiceService::class);
        $first = $svc->createFromTreatmentPlan($this->plan->fresh('items'));
        $svc->void($first);
        $second = $svc->createFromTreatmentPlan($this->plan->fresh('items'));

        $this->assertSame(1, $first->number_seq);
        $this->assertSame(2, $second->number_seq);

        // Another tenant starts at 1 independently.
        $other = Tenant::create(['name' => 'Other', 'slug' => 'other']);
        app(Tenancy::class)->set($other);
        $p = Procedure::factory()->create();
        $plan = TreatmentPlan::factory()->create();
        app(TreatmentPlanService::class)->addItem($plan, ['procedure_id' => $p->id]);

        $this->assertSame(1, $svc->createFromTreatmentPlan($plan->fresh('items'))->number_seq);
    }

    public function test_plan_cannot_be_invoiced_twice_unless_voided(): void
    {
        $svc = app(InvoiceService::class);
        $svc->createFromTreatmentPlan($this->plan->fresh('items'));

        $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class);
        $svc->createFromTreatmentPlan($this->plan->fresh('items'));
    }

    public function test_issue_requires_items_and_only_from_draft(): void
    {
        $svc = app(InvoiceService::class);
        $invoice = $svc->createFromTreatmentPlan($this->plan->fresh('items'));

        $svc->issue($invoice);
        $this->assertSame(InvoiceStatus::Issued, $invoice->fresh()->status);
        $this->assertNotNull($invoice->fresh()->issued_at);

        $this->expectException(\Symfony\Component\HttpKernel\Exception\HttpException::class);
        $svc->issue($invoice->fresh()); // already issued
    }

    public function test_issuing_updates_patient_outstanding_balance(): void
    {
        $svc = app(InvoiceService::class);
        $invoice = $svc->createFromTreatmentPlan($this->plan->fresh('items'));
        $this->assertSame(0, $this->plan->patient->fresh()->outstanding_balance);

        $svc->issue($invoice);
        $this->assertSame(70000, $this->plan->patient->fresh()->outstanding_balance);

        $invoice2 = $svc->createCustom($this->plan->patient, [
            ['description' => 'X-ray', 'qty' => 1, 'unit_price' => 15000],
        ]);
        $svc->issue($invoice2);
        $this->assertSame(85000, $this->plan->patient->fresh()->outstanding_balance);
    }
}
