<?php

declare(strict_types=1);

namespace apexl\Io\modules\product\Tests;

use apexl\Io\modules\product\Entity\Product;
use apexl\Io\modules\product\Service\ProductService;
use apexl\Io\TestInfrastructure\FeatureTest;
use PHPUnit\Framework\Attributes\Test;

/**
 * io-product V3 — catalogue entity + service.
 */
final class ProductServiceTest extends FeatureTest
{
    private function svc(): ProductService
    {
        return self::$application->getContainer()->get(ProductService::class);
    }

    #[Test]
    public function factory_persists_a_product(): void
    {
        $p = Product::factory()->create(['code' => 'FAC-1', 'name' => 'Factory product']);
        self::assertGreaterThan(0, (int) $p->id);
        self::assertSame('Factory product', (string) Product::find((int) $p->id)->name);
    }

    #[Test]
    public function create_update_delete_and_find_by_code(): void
    {
        $svc = $this->svc();

        $p = $svc->create([
            'code' => 'DESIGN-HR', 'name' => 'Design (hourly)', 'kind' => 'service',
            'unit_price' => 9500, 'unit_cost' => 4200, 'default_markup_pct' => 25, 'is_purchased' => true,
        ]);
        self::assertSame(9500, (int) $p->unit_price);
        self::assertSame(25.0, $p->markupPercent());
        self::assertSame(1, (int) $p->is_purchased);

        self::assertNotNull($svc->findByCode('DESIGN-HR'));

        $svc->update($p, ['code' => 'DESIGN-HR', 'name' => 'Design — hourly', 'kind' => 'service', 'is_active' => false]);
        $reloaded = Product::find((int) $p->id);
        self::assertSame('Design — hourly', (string) $reloaded->name);
        self::assertSame(0, (int) $reloaded->is_active);

        $svc->delete($p);
        self::assertNull(Product::find((int) $p->id));
    }

    #[Test]
    public function duplicate_code_is_rejected(): void
    {
        $svc = $this->svc();
        $svc->create(['code' => 'DUP-1', 'name' => 'First']);
        $this->expectException(\InvalidArgumentException::class);
        $svc->create(['code' => 'DUP-1', 'name' => 'Second']);
    }

    #[Test]
    public function requires_code_and_name(): void
    {
        $svc = $this->svc();
        self::assertFalse($this->throws(fn() => $svc->create(['code' => 'X', 'name' => 'Y'])));
        self::assertTrue($this->throws(fn() => $svc->create(['code' => '', 'name' => 'Y'])));
        self::assertTrue($this->throws(fn() => $svc->create(['code' => 'Z', 'name' => ''])));
    }

    private function throws(callable $fn): bool
    {
        try { $fn(); return false; } catch (\InvalidArgumentException) { return true; }
    }
}
