<?php

namespace Tests\Feature\Audit;

use App\Models\Central\Company;
use App\Models\Central\Plan;
use App\Models\Tenant\Client;
use App\Models\Tenant\Part;
use App\Models\Tenant\SubcontractJob;
use App\Models\Tenant\Vehicle;
use App\Models\Tenant\Warehouse;
use App\Models\Tenant\WorkOrder;
use App\Models\Tenant\WorkOrderPart;
use App\Models\Tenant\WorkOrderWork;
use App\Services\Warehouse\WarehouseService;
use App\Tenancy\TenantManager;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

/**
 * The most fragile invariant in the system: WorkOrder.total. Every
 * line type (works + parts + subcontract) feeds into it, plus discount,
 * minus excluded statuses. If recalcTotal forgets ONE source, money disappears.
 */
class WorkOrderTotalsTest extends TestCase
{
    use RefreshDatabase;

    public function test_total_aggregates_all_line_types_and_applies_discount(): void
    {
        $ctx = $this->bootstrap();
        $wo = $this->makeWo($ctx, discountPct: 10);

        // Labor: 2h × 400 = 800
        WorkOrderWork::create([
            'work_order_id' => $wo->id, 'name' => 'Manoperă',
            'hours' => 2, 'price_per_hour' => 400, 'status' => 'todo',
        ]);

        // Part: 3 × 150 = 450
        $part = Part::create([
            'name' => 'Filtru', 'sell_price' => 150, 'buy_price' => 100,
            'qty' => 10, 'unit' => 'buc', 'is_active' => true,
        ]);
        WorkOrderPart::create([
            'work_order_id' => $wo->id, 'part_id' => $part->id,
            'name' => $part->name, 'qty' => 3,
            'buy_price' => 100, 'sell_price' => 150,
            'status' => 'needed',
        ]);

        // Subcontract: cost 500 × 1.20 markup = 600 client price
        SubcontractJob::create([
            'work_order_id' => $wo->id, 'category' => 'Turbo',
            'cost' => 500, 'markup_pct' => 20, 'status' => 'sent',
        ]);

        $wo->refresh();
        // Subtotal: 800 + 450 + 600 = 1850. With 10% discount → 1665.
        $this->assertEquals(1665.0, (float) $wo->total);
    }

    public function test_cancelled_subcontract_excluded_from_total(): void
    {
        $ctx = $this->bootstrap();
        $wo = $this->makeWo($ctx);

        $sub = SubcontractJob::create([
            'work_order_id' => $wo->id, 'cost' => 1000, 'markup_pct' => 50, 'status' => 'sent',
        ]);
        $wo->refresh();
        $this->assertEquals(1500.0, (float) $wo->total);

        $sub->update(['status' => 'cancelled']);
        $wo->refresh();
        $this->assertEquals(0.0, (float) $wo->total);
    }

    public function test_deleting_any_line_type_recalcs_total(): void
    {
        $ctx = $this->bootstrap();
        $wo = $this->makeWo($ctx);

        $w = WorkOrderWork::create([
            'work_order_id' => $wo->id, 'name' => 'X',
            'hours' => 1, 'price_per_hour' => 100, 'status' => 'todo',
        ]);
        $wo->refresh();
        $this->assertEquals(100.0, (float) $wo->total);

        $w->delete();
        $wo->refresh();
        $this->assertEquals(0.0, (float) $wo->total);
    }

    public function test_status_done_consumes_part_reservations(): void
    {
        $ctx = $this->bootstrap();
        $svc = app(WarehouseService::class);

        // Stock the warehouse with one batch of 10.
        $part = Part::create([
            'name' => 'Plăcuțe', 'sell_price' => 200, 'buy_price' => 100,
            'qty' => 0, 'unit' => 'buc', 'is_active' => true,
        ]);
        $svc->receive($part, 10, 100);
        $this->assertEquals(10.0, (float) $part->fresh()->qty);

        $wo = $this->makeWo($ctx);
        WorkOrderPart::create([
            'work_order_id' => $wo->id, 'part_id' => $part->id,
            'name' => $part->name, 'qty' => 4,
            'buy_price' => 100, 'sell_price' => 200,
            'status' => 'needed',
        ]);

        // Before close: stock still 10, qty_reserved 4.
        $part->refresh();
        $this->assertEquals(10.0, (float) $part->qty);
        $this->assertEquals(4.0, (float) $part->qty_reserved);

        // Close WO → reservations become consume events.
        $wo->update(['status' => 'done']);
        $part->refresh();
        $this->assertEquals(6.0, (float) $part->qty);
        $this->assertEquals(0.0, (float) $part->qty_reserved);
    }

    public function test_cancelled_wo_releases_reservations(): void
    {
        $ctx = $this->bootstrap();
        $svc = app(WarehouseService::class);
        $part = Part::create([
            'name' => 'X', 'sell_price' => 10, 'buy_price' => 5,
            'qty' => 0, 'unit' => 'buc', 'is_active' => true,
        ]);
        $svc->receive($part, 5, 5);

        $wo = $this->makeWo($ctx);
        WorkOrderPart::create([
            'work_order_id' => $wo->id, 'part_id' => $part->id,
            'name' => 'X', 'qty' => 2,
            'buy_price' => 5, 'sell_price' => 10,
            'status' => 'needed',
        ]);
        $this->assertEquals(2.0, (float) $part->fresh()->qty_reserved);

        $wo->update(['status' => 'cancelled']);
        $this->assertEquals(0.0, (float) $part->fresh()->qty_reserved);
        $this->assertEquals(5.0, (float) $part->fresh()->qty, 'stock untouched after cancel');
    }

    private function bootstrap(): array
    {
        $plan = Plan::firstOrCreate(['slug' => 'test'], ['name' => 'T', 'price' => 0, 'features' => []]);
        $company = Company::create([
            'plan_id' => $plan->id, 'slug' => 'wot-' . uniqid(),
            'name' => 'WO Totals', 'status' => 'active',
        ]);
        app(TenantManager::class)->setCurrent($company);

        // Ensure warehouse exists so WarehouseService::receive works.
        $wh = Warehouse::create([
            'code' => 'MAIN', 'name' => 'Main',
            'is_default' => true, 'is_active' => true,
        ]);
        $company->forceFill(['default_warehouse_id' => $wh->id])->saveQuietly();

        return compact('company');
    }

    private function makeWo(array $ctx, float $discountPct = 0): WorkOrder
    {
        $client = Client::create([
            'name' => 'C', 'phone' => '+3736' . random_int(1000000, 9999999),
            'type' => 'individual', 'status' => 'active',
        ]);
        $vehicle = Vehicle::create([
            'client_id' => $client->id, 'make' => 'X', 'model' => 'Y',
            'plate' => 'WO' . random_int(100, 999),
        ]);
        return WorkOrder::create([
            'number' => WorkOrder::generateNumber($ctx['company']->id),
            'client_id' => $client->id, 'vehicle_id' => $vehicle->id,
            'opened_at' => today(), 'status' => 'in_work',
            'discount_pct' => $discountPct,
        ]);
    }
}
