<?php

namespace App\Course\Controller;

use App\Auth\Entity\User;
use App\Branch\Service\BranchResolver;
use App\Course\Entity\CourseSession;
use App\Course\Entity\TreatmentCourse;
use App\Course\Repository\CourseProtocolRepository;
use App\Course\Repository\TreatmentCourseRepository;
use App\Course\Service\CourseBooker;
use App\Course\Service\CourseProgressCalculator;
use App\Course\Service\CourseScheduler;
use App\Course\Service\CourseStarter;
use App\Doctor\Repository\DoctorRepository;
use App\Package\Repository\PatientPackageRepository;
use App\Patient\Entity\PatientRecord;
use App\Package\Service\CreditLedgerService;
use App\Patient\Repository\PatientRecordRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Exception\AppException;
use App\Shared\Tenant\TenantOwnershipChecker;
use OpenApi\Attributes as OA;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[OA\Tag(name: 'Course')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class TreatmentCourseController extends BaseController
{
    public function __construct(
        private readonly TreatmentCourseRepository $courses,
        private readonly CourseProtocolRepository  $protocols,
        private readonly PatientRecordRepository   $patients,
        private readonly PatientPackageRepository  $patientPackages,
        private readonly DoctorRepository          $doctors,
        private readonly CourseStarter             $starter,
        private readonly CourseScheduler           $scheduler,
        private readonly CourseBooker              $booker,
        private readonly CourseProgressCalculator  $progress,
        private readonly BranchResolver            $branches,
        private readonly TenantOwnershipChecker    $ownership,
        private readonly CreditLedgerService       $credits,
    ) {}

    #[Route('/api/v1/treatment-course', name: 'treatment_course_create', methods: ['POST'])]
    public function create(#[CurrentUser] User $user, Request $request): JsonResponse
    {
        $data = json_decode($request->getContent(), true);

        if (!is_array($data) || !is_string($data['patient_uuid'] ?? null)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'شناسهٔ بیمار الزامی است', 422, 'patient_uuid');
        }

        if (!is_string($data['protocol_uuid'] ?? null)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'شناسهٔ پروتکل الزامی است', 422, 'protocol_uuid');
        }

        $patient  = $this->requirePatient($user, $data['patient_uuid']);
        $protocol = $this->protocols->findByUuid($data['protocol_uuid']);
        [$entityType, $entityId] = $this->branches->pair($user);

        if ($protocol === null || !$this->ownership->belongsToPair($entityType, $entityId, $protocol)) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پروتکل یافت نشد', 404);
        }

        $package = null;

        if (is_string($data['patient_package_uuid'] ?? null)) {
            $package = $this->patientPackages->findByUuid($data['patient_package_uuid']);

            if ($package === null || !$this->ownership->belongsToPair($entityType, $entityId, $package)) {
                return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پکیج بیمار یافت نشد', 404);
            }
        }

        $course = $this->starter->start($patient, $protocol, $package);

        return $this->success($this->detail($course), 201);
    }

    #[Route('/api/v1/treatment-course/{uuid}', name: 'treatment_course_show', methods: ['GET'])]
    public function show(#[CurrentUser] User $user, string $uuid): JsonResponse
    {
        return $this->success($this->detail($this->requireCourse($user, $uuid)));
    }

    #[Route('/api/v1/patient/{uuid}/courses', name: 'patient_course_index', methods: ['GET'])]
    public function forPatient(#[CurrentUser] User $user, string $uuid): JsonResponse
    {
        $patient = $this->requirePatient($user, $uuid);

        return $this->success(array_map(
            fn (TreatmentCourse $c): array => $c->toArray() + ['progress' => $this->progress->progressOf($c)],
            $this->courses->findForPatient($patient),
        ));
    }

    /**
     * پیشنهاد تاریخ جلسهٔ بعدی — بازهٔ مجاز، تاریخ ایده‌آل و چند وقت نزدیک به آن.
     */
    #[Route('/api/v1/treatment-course/{uuid}/next-slot-suggestion', name: 'treatment_course_next_slot', methods: ['GET'])]
    public function nextSlot(#[CurrentUser] User $user, string $uuid, Request $request): JsonResponse
    {
        $course  = $this->requireCourse($user, $uuid);
        $branch  = $request->query->get('branch_uuid');

        if (!is_string($branch)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'فیلد branch_uuid الزامی است', 422, 'branch_uuid');
        }

        $address = $this->branches->resolve($user, $branch);

        return $this->success($this->scheduler->suggestNext($course, $address));
    }

    /** رزرو همهٔ جلسات باقی‌مانده — همه یا هیچ. */
    #[Route('/api/v1/treatment-course/{uuid}/book-all', name: 'treatment_course_book_all', methods: ['POST'])]
    public function bookAll(#[CurrentUser] User $user, string $uuid, Request $request): JsonResponse
    {
        $course = $this->requireCourse($user, $uuid);
        $data   = json_decode($request->getContent(), true);

        if (!is_array($data) || !is_string($data['branch_uuid'] ?? null)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'فیلد branch_uuid الزامی است', 422, 'branch_uuid');
        }

        if (!is_string($data['doctor_uuid'] ?? null)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'فیلد doctor_uuid الزامی است', 422, 'doctor_uuid');
        }

        $address = $this->branches->resolve($user, $data['branch_uuid']);
        $doctor  = $this->doctors->findOneBy(['uuid' => $data['doctor_uuid']]);

        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404);
        }

        $result = $this->booker->bookAll($course, $address, $doctor, $user);

        return $this->success($result + ['course' => $this->detail($course)]);
    }

    #[Route('/api/v1/treatment-course/{uuid}/abandon', name: 'treatment_course_abandon', methods: ['POST'])]
    public function abandon(#[CurrentUser] User $user, string $uuid, Request $request): JsonResponse
    {
        $course = $this->requireCourse($user, $uuid);
        $data   = json_decode($request->getContent(), true);

        if (!is_array($data) || !is_string($data['reason'] ?? null) || trim($data['reason']) === '') {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دلیل رهاکردن دوره الزامی است', 422, 'reason');
        }

        if (!$course->isActive()) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'این دوره فعال نیست', 422);
        }

        $course->abandon(trim($data['reason']));
        $this->courses->save($course);

        return $this->success($this->detail($course));
    }

    /** @return array<string, mixed> */
    private function detail(TreatmentCourse $course): array
    {
        $sessions = $course->getSessions()->toArray();

        usort($sessions, static fn (CourseSession $a, CourseSession $b): int
            => $a->getSessionNumber() <=> $b->getSessionNumber());

        // مانده در برابر جلسات باقی‌مانده: **هشدار** است نه خطا. دوره‌ای که پکیجش کفاف
        // نمی‌دهد هنوز کاملاً معتبر است — بقیه‌اش نقدی می‌شود — ولی کسی باید بداند،
        // ترجیحاً قبل از جلسهٔ ششم نه سرِ آن.
        $package = $course->getPatientPackage();
        $balance = $package === null ? null : $this->credits->balance($package);
        $needed  = count(array_filter(
            $sessions,
            static fn (CourseSession $s): bool => $s->getStatus() !== CourseSession::STATUS_COMPLETED,
        ));

        return $course->toArray() + [
            'progress'        => $this->progress->progressOf($course),
            'package_balance' => $balance,
            'package_shortfall' => $balance === null ? null : max(0, $needed - $balance),
            'sessions' => array_map(
                static fn (CourseSession $s): array => $s->toArray(),
                $sessions,
            ),
        ];
    }

    private function requirePatient(User $user, string $uuid): PatientRecord
    {
        $patient = $this->patients->findOneBy(['uuid' => $uuid]);
        [$entityType, $entityId] = $this->branches->pair($user);

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

        return $patient;
    }

    private function requireCourse(User $user, string $uuid): TreatmentCourse
    {
        $course = $this->courses->findByUuid($uuid);
        [$entityType, $entityId] = $this->branches->pair($user);

        if ($course === null || !$this->ownership->belongsToPair($entityType, $entityId, $course)) {
            throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'دوره یافت نشد', 404);
        }

        return $course;
    }
}
