<?php

namespace Tests\Feature;

use App\Models\Central\Company;
use App\Models\Central\Plan;
use App\Models\Tenant\Part;
use App\Models\Tenant\PartBatch;
use App\Models\Tenant\Purchase;
use App\Models\Tenant\PurchaseItem;
use App\Models\Tenant\Supplier;
use App\Models\Tenant\SupplierPartPrice;
use App\Models\Tenant\Warehouse;
use App\Models\Tenant\WarehouseEvent;
use App\Services\Warehouse\SupplierAnalytics;
use App\Tenancy\TenantManager;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PurchaseReceiptTest extends TestCase
{
    use RefreshDatabase;

    public function test_partial_receipt_sets_status_partial_and_creates_batch(): void
    {
        $ctx = $this->makeContext();
        ['purchase' => $po, 'item' => $item, 'part' => $part] = $ctx;

        $po->receiveItem($item, 3, $ctx['warehouse']);

        $item->refresh(); $po->refresh(); $part->refresh();
        $this->assertEquals(3.0, (float) $item->qty_received);
        $this->assertEquals('partial', $po->status);
        $this->assertFalse($item->isFullyReceived());
        $this->assertEquals(3.0, (float) $part->qty);
        $this->assertEquals(1, PartBatch::where('part_id', $part->id)->count());
        $this->assertEquals(1, SupplierPartPrice::where('part_id', $part->id)->count());
        $this->assertEquals(1, WarehouseEvent::where('part_id', $part->id)->where('type', 'receipt')->count());
    }

    public function test_full_receipt_via_helper_sets_status_received(): void
    {
        $ctx = $this->makeContext();
        $ctx['purchase']->receiveAllRemaining();

        $ctx['purchase']->refresh(); $ctx['part']->refresh();
        $this->assertEquals('received', $ctx['purchase']->status);
        $this->assertNotNull($ctx['purchase']->received_at);
        $this->assertTrue($ctx['item']->fresh()->isFullyReceived());
        $this->assertEquals(10.0, (float) $ctx['part']->qty);
    }

    public function test_receipt_over_outstanding_throws(): void
    {
        $ctx = $this->makeContext();
        $this->expectException(\InvalidArgumentException::class);
        $ctx['purchase']->receiveItem($ctx['item'], 11);
    }

    public function test_two_partial_receipts_complete_purchase(): void
    {
        $ctx = $this->makeContext();
        $ctx['purchase']->receiveItem($ctx['item'], 4);
        $ctx['purchase']->refresh();
        $this->assertEquals('partial', $ctx['purchase']->status);

        $ctx['purchase']->receiveItem($ctx['item']->fresh(), 6);
        $ctx['purchase']->refresh();
        $this->assertEquals('received', $ctx['purchase']->status);

        // Two separate batches → FIFO will consume the first one first.
        $this->assertEquals(2, PartBatch::where('part_id', $ctx['part']->id)->count());
    }

    public function test_supplier_analytics_on_time_rate(): void
    {
        $ctx = $this->makeContext();
        $analytics = app(SupplierAnalytics::class);

        // No purchases received yet → null
        $this->assertNull($analytics->onTimeRate($ctx['supplier']));

        // One on-time delivery
        $ctx['purchase']->update(['expected_at' => now()->addDays(5), 'received_at' => now()->addDays(3)]);
        $ctx['purchase']->refresh();
        $ctx['purchase']->update(['status' => 'received']);

        // Second one late
        $po2 = Purchase::create([
            'number' => Purchase::generateNumber($ctx['supplier']->company_id),
            'supplier_id' => $ctx['supplier']->id,
            'warehouse_id' => $ctx['warehouse']->id,
            'order_date' => now()->subDays(10),
            'expected_at' => now()->subDays(2),
            'received_at' => now(),
            'status' => 'received',
        ]);
        $po2->refresh();

        $rate = $analytics->onTimeRate($ctx['supplier']);
        $this->assertEquals(50.0, $rate, '1 on-time + 1 late = 50%');
    }

    public function test_computed_rating_returns_null_with_insufficient_data(): void
    {
        $ctx = $this->makeContext();
        $analytics = app(SupplierAnalytics::class);
        $this->assertNull($analytics->computedRating($ctx['supplier']));
    }

    private function makeContext(): array
    {
        $plan = Plan::firstOrCreate(['slug' => 'test'], [
            'name' => 'Test', 'price' => 0, 'features' => [],
        ]);
        $company = Company::create([
            'plan_id' => $plan->id,
            'slug' => 'pr-' . uniqid(),
            'name' => 'PR Service',
            'status' => 'active',
        ]);
        app(TenantManager::class)->setCurrent($company);

        $warehouse = Warehouse::create([
            'code' => 'MAIN', 'name' => 'Depozit',
            'is_default' => true, 'is_active' => true,
        ]);
        $company->forceFill(['default_warehouse_id' => $warehouse->id])->saveQuietly();

        $supplier = Supplier::create([
            'name' => 'Acme Parts',
            'is_active' => true,
            'rating' => 3,
            'delivery_days' => 0,
            'discount_pct' => 0,
        ]);

        $part = Part::create([
            'name' => 'Filtru aer',
            'article' => 'FA-001',
            'unit' => 'buc',
            'qty' => 0,
            'buy_price' => 0,
            'sell_price' => 0,
            'is_active' => true,
        ]);

        $purchase = Purchase::create([
            'number' => Purchase::generateNumber($company->id),
            'supplier_id' => $supplier->id,
            'warehouse_id' => $warehouse->id,
            'order_date' => now(),
            'expected_at' => now()->addDays(3),
            'status' => 'ordered',
        ]);

        $item = PurchaseItem::create([
            'purchase_id' => $purchase->id,
            'part_id' => $part->id,
            'name' => $part->name,
            'article' => $part->article,
            'qty' => 10,
            'unit' => 'buc',
            'buy_price' => 50,
        ]);

        return compact('company', 'warehouse', 'supplier', 'part', 'purchase', 'item');
    }
}
