<?php

namespace Tests\Unit;

use App\Exceptions\CouponPoolExhaustedException;
use App\Models\Coupon;
use App\Services\CouponCodePool;
use App\Services\CouponService;
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
use Mockery;
use Mockery\MockInterface;
use Tests\TestCase;

class CouponServiceTest extends TestCase
{
    use LazilyRefreshDatabase;

    public function test_find_or_create_assigns_code_from_pool(): void
    {
        $this->mock(CouponCodePool::class, function (MockInterface $mock): void {
            $mock->shouldReceive('pickRandom')->once()->andReturn('1_AAAA');
        });

        $coupon = app(CouponService::class)->findOrCreateForPhone('61929248');

        $this->assertSame('61929248', $coupon->phone);
        $this->assertSame('1_AAAA', $coupon->code);
        $this->assertDatabaseHas(Coupon::class, [
            'phone' => '61929248',
            'code' => '1_AAAA',
        ]);
    }

    public function test_find_or_create_returns_existing_coupon_without_picking(): void
    {
        $existing = Coupon::factory()->create(['phone' => '61929248', 'code' => '2_BBBB']);

        $this->mock(CouponCodePool::class, function (MockInterface $mock): void {
            $mock->shouldNotReceive('pickRandom');
        });

        $coupon = app(CouponService::class)->findOrCreateForPhone('61929248');

        $this->assertTrue($existing->is($coupon));
    }

    public function test_find_or_create_throws_when_pool_exhausted(): void
    {
        $this->mock(CouponCodePool::class, function (MockInterface $mock): void {
            $mock->shouldReceive('pickRandom')->andReturn(null);
        });

        $this->expectException(CouponPoolExhaustedException::class);

        app(CouponService::class)->findOrCreateForPhone('61929248');
    }

    protected function tearDown(): void
    {
        Mockery::close();

        parent::tearDown();
    }
}
