<?php

namespace Tests\Unit\Catalog;

use App\Domains\Catalog\Models\Inventory;
use App\Domains\Catalog\Models\StockReservation;
use App\Domains\Catalog\Models\StockReservationLine;
use App\Domains\Catalog\Services\ExpireStockReservationsService;
use App\Domains\Catalog\Services\StockReservationService;
use App\Domains\Purchase\Services\Checkout\ReleaseCheckoutService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use RuntimeException;
use Tests\TestCase;

class ExpireStockReservationsServiceTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_expires_an_orphan_reservation_and_releases_its_inventory(): void
    {
        $inventory = Inventory::query()->create([
            'real_stock' => 10,
            'reserved_stock' => 2,
        ]);
        $reservation = StockReservation::query()->create([
            'status' => StockReservation::STATUS_ACTIVE,
            'expires_at' => now()->subMinute(),
        ]);
        StockReservationLine::query()->create([
            'stock_reservation_id' => $reservation->id,
            'inventory_id' => $inventory->id,
            'quantity' => 2,
            'tracks_inventory' => true,
        ]);

        $result = app(ExpireStockReservationsService::class)->expireOverdue();

        $this->assertSame([
            'purchases' => 0,
            'cart_reservations' => 0,
            'orphan_reservations' => 1,
            'failed' => 0,
        ], $result);
        $this->assertDatabaseHas('stock_reservations', [
            'id' => $reservation->id,
            'status' => StockReservation::STATUS_EXPIRED,
            'expires_at' => null,
        ]);
        $this->assertDatabaseHas('inventories', [
            'id' => $inventory->id,
            'reserved_stock' => 0,
        ]);
    }

    public function test_one_failed_reservation_does_not_stop_the_remaining_batch(): void
    {
        $first = StockReservation::query()->create([
            'status' => StockReservation::STATUS_ACTIVE,
            'expires_at' => now()->subMinutes(2),
        ]);
        $second = StockReservation::query()->create([
            'status' => StockReservation::STATUS_ACTIVE,
            'expires_at' => now()->subMinute(),
        ]);

        $reservations = \Mockery::mock(StockReservationService::class);
        $reservations->shouldReceive('expire')
            ->twice()
            ->andReturnUsing(function (StockReservation $reservation) use ($first): void {
                if ($reservation->is($first)) {
                    throw new RuntimeException('Broken reservation.');
                }

                $reservation->update([
                    'status' => StockReservation::STATUS_EXPIRED,
                    'expires_at' => null,
                    'expired_at' => now(),
                ]);
            });

        $service = new ExpireStockReservationsService(
            \Mockery::mock(ReleaseCheckoutService::class),
            $reservations,
        );

        $result = $service->expireOverdue();

        $this->assertSame([
            'purchases' => 0,
            'cart_reservations' => 0,
            'orphan_reservations' => 1,
            'failed' => 1,
        ], $result);
        $this->assertDatabaseHas('stock_reservations', [
            'id' => $first->id,
            'status' => StockReservation::STATUS_ACTIVE,
        ]);
        $this->assertDatabaseHas('stock_reservations', [
            'id' => $second->id,
            'status' => StockReservation::STATUS_EXPIRED,
        ]);
    }
}
