<?php

namespace App\ClinicService\Service;

use App\Auth\Entity\User;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceItemAuditLog;
use App\ClinicService\Repository\ServiceItemAuditLogRepository;

/**
 * ثبت تاریخچه‌ی تغییرات خدمات. کنترلر فقط snapshot قبل و بعد را می‌دهد؛ تشخیص
 * فیلدهای تغییریافته اینجا انجام می‌شود.
 */
class ServiceItemAuditService
{
    /** فیلدهایی که تغییرشان لاگ می‌شود (کلید = نام فیلد در لاگ). */
    private const TRACKED = [
        'name'              => 'نام سرویس',
        'price_rials'       => 'قیمت پایه',
        'active'            => 'وضعیت',
        'duration_minutes'  => 'زمان متوسط',
        'bookable'          => 'نمایش در نوبت‌دهی',
        'insurance_covered' => 'پوشش بیمه',
        'service_category'  => 'نوع خدمت',
        'inventory_package' => 'پکیج کالا',
        'consumables'       => 'کالاهای تکی',
    ];

    public function __construct(private readonly ServiceItemAuditLogRepository $repo) {}

    /** @return array<string, string|null> snapshot قابل مقایسه از وضعیت فعلی خدمت. */
    public function snapshot(ServiceItem $item): array
    {
        return [
            'name'              => $item->getName(),
            'price_rials'       => (string) $item->getPriceRials(),
            'active'            => $item->isActive() ? '1' : '0',
            'duration_minutes'  => $item->getDurationMinutes() === null ? null : (string) $item->getDurationMinutes(),
            'bookable'          => $item->isBookable() ? '1' : '0',
            'insurance_covered' => $item->isInsuranceCovered() ? '1' : '0',
            'service_category'  => $item->getServiceCategory()->label(),
            'inventory_package' => $item->getInventoryPackageId() === null ? null : (string) $item->getInventoryPackageId(),
            'consumables'       => $this->consumablesFingerprint($item),
        ];
    }

    /** امضای مرتب‌شده‌ی اقلام تکی تا تغییر در قلم یا تعداد قابل تشخیص باشد. */
    private function consumablesFingerprint(ServiceItem $item): ?string
    {
        $lines = [];
        foreach ($item->getConsumables() as $consumable) {
            $lines[] = $consumable->getItem()->getName() . '×' . $consumable->getAmount();
        }

        if ($lines === []) {
            return null;
        }

        sort($lines);

        return implode('، ', $lines);
    }

    public function logCreate(ServiceItem $item, ?User $actor): void
    {
        $this->repo->save(
            (new ServiceItemAuditLog($item, 'name', ServiceItemAuditLog::OP_CREATE))
                ->setActor($actor?->getId(), $this->actorName($actor))
                ->setValues(null, $item->getName())
        );
    }

    /**
     * تفاوت دو snapshot را لاگ می‌کند. فیلد بدون تغییر ردیف نمی‌سازد.
     *
     * @param array<string, string|null> $before
     * @param array<string, string|null> $after
     */
    public function logChanges(ServiceItem $item, array $before, array $after, ?User $actor): void
    {
        $logged = false;

        foreach (array_keys(self::TRACKED) as $field) {
            if (($before[$field] ?? null) === ($after[$field] ?? null)) {
                continue;
            }

            $this->repo->save(
                (new ServiceItemAuditLog($item, $field, ServiceItemAuditLog::OP_UPDATE))
                    ->setActor($actor?->getId(), $this->actorName($actor))
                    ->setValues($before[$field] ?? null, $after[$field] ?? null),
                false,
            );
            $logged = true;
        }

        if ($logged) {
            $this->repo->flush();
        }
    }

    private function actorName(?User $actor): ?string
    {
        if ($actor === null) {
            return null;
        }

        return $actor->getRealName() ?: $actor->getMobileNumber();
    }
}
