<?php

namespace App\ClinicInvitation\Service;

use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\ClinicInvitation\Entity\ClinicDoctorInvitation;
use App\ClinicInvitation\Repository\ClinicDoctorInvitationRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Exception\AppException;
use App\Sms\Service\SmsService;
use Doctrine\ORM\EntityManagerInterface;

class ClinicInvitationService
{
    public function __construct(
        private readonly ClinicDoctorInvitationRepository $repo,
        private readonly DoctorRepository                 $doctorRepo,
        private readonly SmsService                       $smsService,
        private readonly EntityManagerInterface           $em,
        private readonly string                           $appUrl,
    ) {}

    public function invite(Clinic $clinic, User $invitedBy, string $mobile, ?string $name, ?string $specialty): ClinicDoctorInvitation
    {
        $existing = $this->repo->findPendingByMobileAndClinic($mobile, $clinic->getId());
        if ($existing !== null) {
            throw new AppException('ERR_CONFLICT_001', 'این شماره قبلاً برای این کلینیک دعوت شده است', 409);
        }

        $inv = new ClinicDoctorInvitation($clinic, $invitedBy, $mobile);
        $inv->setInvitedName($name);
        $inv->setInvitedSpecialty($specialty);

        $doctor = $this->doctorRepo->findOneByMobile($mobile);
        if ($doctor !== null) {
            $inv->setDoctor($doctor);
        }

        $this->repo->save($inv);
        $this->sendSms($inv, $clinic);

        return $inv;
    }

    public function resend(ClinicDoctorInvitation $inv): void
    {
        if (in_array($inv->getStatus(), [ClinicDoctorInvitation::STATUS_REMOVED, ClinicDoctorInvitation::STATUS_ACCEPTED], true)) {
            throw new AppException('ERR_CONFLICT_001', 'امکان ارسال مجدد دعوتنامه وجود ندارد', 409);
        }

        $inv->refresh();
        $this->em->flush();
        $this->sendSms($inv, $inv->getClinic());
    }

    public function changeStatus(ClinicDoctorInvitation $inv, string $status): void
    {
        $allowed = [ClinicDoctorInvitation::STATUS_SUSPENDED, ClinicDoctorInvitation::STATUS_REMOVED];
        if (!in_array($status, $allowed, true)) {
            throw new AppException('ERR_VALIDATION_001', 'وضعیت نامعتبر است', 422);
        }
        $inv->setStatus($status);
        $this->em->flush();
    }

    public function delete(ClinicDoctorInvitation $inv): void
    {
        $this->em->remove($inv);
        $this->em->flush();
    }

    public function accept(ClinicDoctorInvitation $inv): void
    {
        if (!$inv->isUsable()) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه منقضی یا غیرمعتبر است', 410);
        }

        $inv->setStatus(ClinicDoctorInvitation::STATUS_ACCEPTED);
        $inv->markUsed();

        $doctor = $inv->getDoctor();
        if ($doctor === null) {
            $doctor = $this->doctorRepo->findOneByMobile($inv->getMobile());
            if ($doctor !== null) {
                $inv->setDoctor($doctor);
            }
        }

        if ($doctor !== null) {
            $clinic = $inv->getClinic();
            if (!$clinic->getDoctors()->contains($doctor)) {
                $clinic->getDoctors()->add($doctor);
            }
        }

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

    public function reject(ClinicDoctorInvitation $inv): void
    {
        if (!$inv->isUsable()) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه منقضی یا غیرمعتبر است', 410);
        }
        $inv->setStatus(ClinicDoctorInvitation::STATUS_REJECTED);
        $inv->markUsed();
        $this->em->flush();
    }

    private function sendSms(ClinicDoctorInvitation $inv, Clinic $clinic): void
    {
        $clinicName = $clinic->getName() ?? 'کلینیک';
        $link = rtrim($this->appUrl, '/') . '/i/' . $inv->getToken();

        $this->smsService->dispatchTemplate(\App\Sms\Entity\SmsLog::TAG_CLINIC_INVITATION, $inv->getMobile(), [
            'clinic' => $clinicName,
            'link'   => $link,
        ]);
    }
}
