<?php

namespace App\Tests\ClinicService;

use App\ClinicService\Service\DurationCalculator;
use PHPUnit\Framework\TestCase;

/**
 * فرمول مدت — واحد و بدون دیتابیس.
 *
 * بند ۵ مستند جمع ساده را رد می‌کند. این تست همان مثال مستند را قفل می‌کند و
 * مهم‌تر: تضمین می‌کند نتیجه به **ترتیب انتخاب** وابسته نباشد.
 */
class DurationCalculatorTest extends TestCase
{
    private DurationCalculator $calculator;

    protected function setUp(): void
    {
        $this->calculator = new DurationCalculator();
    }

    /** آیتم ساختگی بدون دیتابیس: فقط چیزی که فرمول می‌خواند. */
    private function item(int $id, ?int $solo, ?int $additional, int $price = 0): object
    {
        return new class ($id, $solo, $additional, $price) extends \App\ClinicService\Entity\ServiceItem {
            public function __construct(
                private readonly int $fakeId,
                private readonly ?int $solo,
                private readonly ?int $additional,
                private readonly int $price,
            ) {}

            public function getId(): ?int { return $this->fakeId; }
            public function getUuid(): string { return 'item-' . $this->fakeId; }
            public function getName(): string { return 'آیتم ' . $this->fakeId; }
            public function getPriceRials(): int { return $this->price; }
            public function getSoloDurationMinutes(): ?int { return $this->solo; }
            public function effectiveAdditionalMinutes(): ?int { return $this->additional ?? $this->solo; }
        };
    }

    /** مثال خودِ مستند: صورت (۱۵/۸) + بیکینی (۱۲/۸) = ۲۳، نه ۲۷. */
    public function testDocumentExample(): void
    {
        $total = $this->calculator->totalMinutes([
            $this->item(1, 15, 8),
            $this->item(2, 12, 8),
        ]);

        self::assertSame(23, $total);
    }

    public function testASingleItemUsesItsSoloDuration(): void
    {
        self::assertSame(12, $this->calculator->totalMinutes([$this->item(2, 12, 8)]));
    }

    /**
     * ترتیب انتخاب نباید مدت را عوض کند — وگرنه بیمار با جابه‌جا کردن کلیک‌ها وقت
     * کوتاه‌تر می‌خرید.
     */
    public function testResultIsIndependentOfSelectionOrder(): void
    {
        $a = $this->calculator->totalMinutes([$this->item(1, 15, 8), $this->item(2, 12, 8)]);
        $b = $this->calculator->totalMinutes([$this->item(2, 12, 8), $this->item(1, 15, 8)]);

        self::assertSame($a, $b);
        self::assertSame(23, $b);
    }

    /** آیتم بدون «مدت اضافه» همان مدت تنها را می‌گیرد — رفتار دادهٔ موجود. */
    public function testMissingAdditionalFallsBackToSoloAndBehavesLikeAPlainSum(): void
    {
        $total = $this->calculator->totalMinutes([
            $this->item(1, 15, null),
            $this->item(2, 12, null),
        ]);

        self::assertSame(27, $total, 'دادهٔ قدیمی دقیقاً مثل قبل جمع ساده می‌شود');
    }

    public function testEmptySelectionIsZero(): void
    {
        self::assertSame(0, $this->calculator->totalMinutes([]));
    }

    /** مدت دستیِ کاربر بر فرمول مقدم است و پایه‌ای برای آن نمی‌شود. */
    public function testExplicitOverrideWins(): void
    {
        $total = $this->calculator->totalMinutes(
            [$this->item(1, 15, 8), $this->item(2, 12, 8)],
            [],
            [1 => 40],
        );

        self::assertSame(48, $total, '۴۰ دستی به‌عنوان لنگر + ۸ اضافهٔ دومی');
    }

    public function testBreakdownNamesTheAnchor(): void
    {
        $rows = $this->calculator->breakdown([
            $this->item(2, 12, 8),
            $this->item(1, 15, 8),
        ]);

        $byName = array_column($rows, 'counted_as', 'item_uuid');

        self::assertSame('solo', $byName['item-1'], 'بزرگ‌ترین مدت تنها لنگر است');
        self::assertSame('additional', $byName['item-2']);
    }

    public function testPriceIsAPlainSum(): void
    {
        $total = $this->calculator->totalPriceRials([
            $this->item(1, 15, 8, 500_000),
            $this->item(2, 12, 8, 300_000),
        ]);

        self::assertSame(800_000, $total, 'قیمت برخلاف مدت جمع ساده است');
    }
}
