<?php

namespace App\Course\Service;

use App\Appointment\Entity\Appointment;
use App\Course\Entity\CourseSession;
use App\Course\Entity\TreatmentCourse;
use App\Course\Repository\CourseSessionRepository;
use App\Shared\Event\DomainEventPublisher;
use App\Shared\Event\DomainEvents;
use Doctrine\ORM\EntityManagerInterface;

/**
 * تنها جایی که پیوند «نوبت ↔ جلسهٔ دوره» نوشته می‌شود.
 *
 * پیوند دوطرفه است (`course_sessions.appointment_id` و `appointments.course_session_id`)
 * تا لیست نوبت‌های پنل بدون JOIN بفهمد نوبت جزو دوره است و صفحهٔ دوره بدون JOIN نوبت را
 * پیدا کند. دو ستون یعنی دو فرصت برای واگرایی، پس **فقط این کلاس** می‌نویسدشان.
 */
final class CourseSessionLinker
{
    public function __construct(
        private readonly CourseSessionRepository $sessions,
        private readonly DomainEventPublisher    $events,
        private readonly EntityManagerInterface  $em,
    ) {}

    public function link(CourseSession $session, Appointment $appointment): void
    {
        $session->markBooked($appointment);
        $appointment->setCourseSession($session);

        $this->em->flush();
    }

    /**
     * لغو نوبت: همان جلسه به `planned` برمی‌گردد و بقیهٔ دوره دست‌نخورده می‌ماند.
     *
     * @return bool `false` یعنی این نوبت اصلاً جزو دوره‌ای نبود
     */
    public function unlink(Appointment $appointment): bool
    {
        $session = $this->sessions->findForAppointment($appointment);

        if ($session === null) {
            return false;
        }

        $session->unbook();
        $appointment->setCourseSession(null);

        $this->em->flush();

        return true;
    }

    /**
     * جلسه انجام شد. دوره وقتی کامل می‌شود که **همهٔ** جلساتش تمام شده باشند —
     * نه وقتی آخرین جلسه رزرو شد.
     */
    public function complete(Appointment $appointment, ?int $at = null): bool
    {
        $session = $this->sessions->findForAppointment($appointment);

        if ($session === null) {
            return false;
        }

        $session->markCompleted($at);

        $course = $session->getCourse();

        $this->events->record(
            $course->getEntityType(),
            $course->getEntityId(),
            DomainEvents::COURSE_SESSION_COMPLETED,
            [
                'course_uuid'    => $course->getUuid(),
                'session_uuid'   => $session->getUuid(),
                'session_number' => $session->getSessionNumber(),
            ],
            $at,
        );

        if ($course->completedCount() >= $course->getSessionCount()) {
            $course->complete($at);

            $this->events->record(
                $course->getEntityType(),
                $course->getEntityId(),
                DomainEvents::COURSE_COMPLETED,
                ['course_uuid' => $course->getUuid()],
                $at,
            );
        }

        $this->em->flush();

        return true;
    }

    public function courseOf(Appointment $appointment): ?TreatmentCourse
    {
        return $this->sessions->findForAppointment($appointment)?->getCourse();
    }
}
