<?php

namespace Tests\Unit;

use App\Exceptions\InsufficientStockException;
use App\Exceptions\StockException;
use App\Models\Product;
use App\Models\StockReservation;
use App\Services\InventoryService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class InventoryServiceTest extends TestCase
{
    use RefreshDatabase;

    protected InventoryService $inventoryService;

    protected function setUp(): void
    {
        parent::setUp();
        $this->inventoryService = new InventoryService();
    }

    public function test_check_availability_returns_true_when_stock_sufficient(): void
    {
        $product = Product::factory()->create(['stock' => 10]);

        $result = $this->inventoryService->checkAvailability($product->id, 5);

        $this->assertTrue($result);
    }

    public function test_check_availability_returns_false_when_stock_insufficient(): void
    {
        $product = Product::factory()->create(['stock' => 5]);

        $result = $this->inventoryService->checkAvailability($product->id, 10);

        $this->assertFalse($result);
    }

    public function test_reserve_stock_creates_reservation(): void
    {
        $product = Product::factory()->create(['stock' => 10]);

        $token = $this->inventoryService->reserve($product->id, 5);

        $this->assertNotEmpty($token);
        $this->assertDatabaseHas('stock_reservations', [
            'product_id' => $product->id,
            'quantity' => 5,
            'status' => 'pending',
        ]);
    }

    public function test_reserve_stock_throws_exception_when_insufficient(): void
    {
        $product = Product::factory()->create(['stock' => 5]);

        $this->expectException(InsufficientStockException::class);

        $this->inventoryService->reserve($product->id, 10);
    }

    public function test_confirm_reservation_decrements_stock(): void
    {
        $product = Product::factory()->create(['stock' => 10]);
        $token = $this->inventoryService->reserve($product->id, 5);

        $this->inventoryService->confirmReservation($token);

        $product->refresh();
        $this->assertEquals(5, $product->stock);
        $this->assertDatabaseHas('stock_reservations', [
            'reservation_token' => $token,
            'status' => 'confirmed',
        ]);
    }

    public function test_release_reservation_updates_status(): void
    {
        $product = Product::factory()->create(['stock' => 10]);
        $token = $this->inventoryService->reserve($product->id, 5);

        $this->inventoryService->release($token);

        $this->assertDatabaseHas('stock_reservations', [
            'reservation_token' => $token,
            'status' => 'released',
        ]);
    }

    public function test_get_available_stock_considers_reservations(): void
    {
        $product = Product::factory()->create(['stock' => 10]);
        
        // Reserve 5 units
        $this->inventoryService->reserve($product->id, 5);

        // Available should be 5 (10 total - 5 reserved)
        $available = $this->inventoryService->getAvailableStock($product->id);
        
        $this->assertEquals(5, $available);
    }

    public function test_decrement_stock_with_lock(): void
    {
        $product = Product::factory()->create(['stock' => 10]);

        $result = $this->inventoryService->decrement($product->id, 5);

        $this->assertTrue($result);
        $product->refresh();
        $this->assertEquals(5, $product->stock);
    }

    public function test_decrement_stock_throws_exception_when_insufficient(): void
    {
        $product = Product::factory()->create(['stock' => 5]);

        $this->expectException(InsufficientStockException::class);

        $this->inventoryService->decrement($product->id, 10);
    }
}

