<?php

namespace App\ClinicService\Service;

use App\ClinicService\Entity\ServiceBranchOverride;
use App\ClinicService\Entity\ServiceItem;

/**
 * مدت واقعیِ یک مجموعه آیتم.
 *
 * بند ۵ مستند جمع سادهٔ مدت‌ها را رد می‌کند: «صورت + بیکینی» ۱۵+۱۲=۲۷ نیست، ۱۵+۸=۲۳
 * است — آماده‌سازی و استقرار بیمار دو بار انجام نمی‌شود. هفت دقیقهٔ هدررفته ضرب در
 * روزی ۲۰ نوبت یعنی یک ساعت ظرفیت در روز.
 *
 * ## چرا «بزرگ‌ترین مدتِ تنها» لنگر است
 *
 * فرمول: یک آیتم با **مدت تنها** حساب می‌شود و بقیه با **مدت اضافه**. کدام آیتم؟
 * آنکه بزرگ‌ترین مدت تنها را دارد. دو دلیل:
 *
 *   ۱. مستقل از ترتیب انتخاب کاربر است. اگر «اولین انتخاب‌شده» لنگر می‌شد، همان سبد
 *      با ترتیب دیگر مدت دیگری می‌گرفت و بیمار با عوض کردن ترتیب کلیک، وقت کوتاه‌تر
 *      می‌خرید.
 *   ۲. محافظه‌کارانه است: هیچ ترکیبی کمتر از واقعیت تخمین زده نمی‌شود، و کم‌تخمینی
 *      یعنی نوبت بعدی روی این یکی می‌افتد.
 */
final class DurationCalculator
{
    /**
     * @param ServiceItem[] $items
     * @param array<int, ServiceBranchOverride> $overrides کلید = شناسهٔ آیتم
     * @param array<int, int> $explicit مدتِ دستیِ کاربر per آیتم — بر همه‌چیز مقدم است
     *                                  و هم «تنها» و هم «اضافه» را جایگزین می‌کند،
     *                                  چون کاربری که مدت را دستی نوشته منظورش همان
     *                                  عدد است، نه پایه‌ای برای فرمول
     */
    public function totalMinutes(array $items, array $overrides = [], array $explicit = []): int
    {
        $solos       = [];
        $additionals = [];

        foreach ($items as $item) {
            $override = $overrides[(int) $item->getId()] ?? null;
            $manual   = $explicit[(int) $item->getId()] ?? null;

            if ($manual !== null) {
                $solos[]       = $manual;
                $additionals[] = $manual;
                continue;
            }

            $solo = $override?->getSoloDurationMinutes() ?? $item->getSoloDurationMinutes() ?? 0;

            // مدت اضافه اگر تعریف نشده باشد، همان مدت تنهاست — پس دادهٔ موجودی که
            // فقط یک عدد داشت، دقیقاً مثل قبل (جمع ساده) حساب می‌شود.
            $additional = $override?->getAdditionalDurationMinutes()
                ?? $item->effectiveAdditionalMinutes()
                ?? $solo;

            $solos[]       = $solo;
            $additionals[] = $additional;
        }

        if ($solos === []) {
            return 0;
        }

        $anchorIndex = array_keys($solos, max($solos), true)[0];
        $total       = $solos[$anchorIndex];

        foreach ($additionals as $index => $additional) {
            if ($index !== $anchorIndex) {
                $total += $additional;
            }
        }

        return $total;
    }

    /**
     * @param ServiceItem[] $items
     * @param array<int, ServiceBranchOverride> $overrides
     */
    public function totalPriceRials(array $items, array $overrides = []): int
    {
        $total = 0;

        foreach ($items as $item) {
            $override = $overrides[(int) $item->getId()] ?? null;
            $total   += $override?->getPriceRials() ?? $item->getPriceRials();
        }

        return $total;
    }

    /**
     * تفکیک per آیتم، برای اینکه UI بتواند نشان دهد چرا جمع با انتظار فرق دارد.
     *
     * @param ServiceItem[] $items
     * @param array<int, ServiceBranchOverride> $overrides
     * @return list<array<string, mixed>>
     */
    public function breakdown(array $items, array $overrides = []): array
    {
        if ($items === []) {
            return [];
        }

        // `$overrides[$id]?->` روی کلیدِ نبوده اخطار «Undefined array key» می‌دهد؛
        // `?? null` لازم است، نه فقط عملگر ایمنِ nullsafe.
        $solos = array_map(
            fn (ServiceItem $i): int => ($overrides[(int) $i->getId()] ?? null)?->getSoloDurationMinutes()
                ?? $i->getSoloDurationMinutes()
                ?? 0,
            $items,
        );

        $anchorIndex = array_keys($solos, max($solos), true)[0];
        $rows        = [];

        foreach ($items as $index => $item) {
            $override = $overrides[(int) $item->getId()] ?? null;
            $isAnchor = $index === $anchorIndex;

            $rows[] = [
                'item_uuid'   => $item->getUuid(),
                'item_name'   => $item->getName(),
                'counted_as'  => $isAnchor ? 'solo' : 'additional',
                'minutes'     => $isAnchor
                    ? $solos[$index]
                    : ($override?->getAdditionalDurationMinutes() ?? $item->effectiveAdditionalMinutes() ?? $solos[$index]),
                'price_rials' => $override?->getPriceRials() ?? $item->getPriceRials(),
            ];
        }

        return $rows;
    }
}
