<?php

namespace App\Course\Service;

use App\Course\Entity\CourseProtocol;
use App\Course\Entity\CourseSession;
use App\Course\Entity\TreatmentCourse;
use App\Course\Repository\TreatmentCourseRepository;
use App\Package\Entity\PatientPackage;
use App\Patient\Entity\PatientRecord;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Event\DomainEventPublisher;
use App\Shared\Event\DomainEvents;
use App\Shared\Exception\AppException;

/**
 * شروع دوره از روی پروتکل.
 *
 * همهٔ جلسات **همین لحظه** ساخته می‌شوند (با وضعیت `planned`) نه هنگام رزرو: بیمار باید
 * از روز اول ببیند «۸ جلسه» یعنی چه، و پارامتر هر جلسه هم همان لحظه از پروتکل کپی
 * می‌شود تا تغییر بعدی پروتکل پروندهٔ او را عوض نکند.
 */
final class CourseStarter
{
    public function __construct(
        private readonly TreatmentCourseRepository $courses,
        private readonly DomainEventPublisher      $events,
    ) {}

    public function start(
        PatientRecord $patient,
        CourseProtocol $protocol,
        ?PatientPackage $package = null,
    ): TreatmentCourse {
        if (!$protocol->isActive()) {
            throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'این پروتکل غیرفعال است', 422, 'protocol_uuid');
        }

        if ($patient->getEntityType() !== $protocol->getEntityType()
            || $patient->getEntityId() !== $protocol->getEntityId()
        ) {
            throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'بیمار یافت نشد', 404);
        }

        $existing = $this->courses->findActiveFor($patient, $protocol->getServiceItem());

        if ($existing !== null) {
            // پیام شامل شناسهٔ دورهٔ موجود است تا اپراتور بتواند مستقیم برود سراغش،
            // نه اینکه دنبالش بگردد.
            throw new AppException(
                ErrorCodes::ERR_VALIDATION_001,
                sprintf('این بیمار یک دورهٔ فعال برای همین خدمت دارد (%s)', $existing->getUuid()),
                422,
                'course_uuid',
            );
        }

        $course = new TreatmentCourse($patient, $protocol);

        if ($package !== null) {
            $this->assertPackageCovers($package, $protocol);
            $course->setPatientPackage($package);
        }

        for ($number = 1; $number <= $protocol->getSessionCount(); $number++) {
            new CourseSession($course, $number, $protocol->paramsFor($number));
        }

        $this->events->record(
            $course->getEntityType(),
            $course->getEntityId(),
            DomainEvents::COURSE_STARTED,
            ['course_uuid' => $course->getUuid(), 'session_count' => $course->getSessionCount()],
        );

        $this->courses->save($course);

        return $course;
    }

    /** پکیجی که این خدمت را پوشش نمی‌دهد، به این دوره وصل نمی‌شود. */
    private function assertPackageCovers(PatientPackage $package, CourseProtocol $protocol): void
    {
        $serviceId = (int) $protocol->getServiceItem()->getId();

        if (!in_array($serviceId, $package->getPackage()->serviceIds(), true)) {
            throw new AppException(
                ErrorCodes::ERR_VALIDATION_001,
                'پکیج انتخاب‌شده این خدمت را پوشش نمی‌دهد',
                422,
                'patient_package_uuid',
            );
        }
    }
}
