<?php

namespace App\Appointment\Command;

use App\Appointment\Entity\Appointment;
use App\Appointment\Repository\AppointmentRepository;
use App\Appointment\Service\AppointmentConfirmationService;
use App\Patient\Repository\PatientSessionRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

/**
 * Finds appointments that were confirmed without getting a patient session.
 *
 * Until the confirmation side-effects were funnelled through
 * AppointmentConfirmationService, only the two PATCH endpoints created records —
 * anything confirmed by an online payment never did. Those appointments are
 * still missing their case file, and completed ones need it just as much: a
 * visit that was never filed does not stop mattering because time passed.
 */
#[AsCommand(name: 'app:appointment:backfill-sessions', description: 'Report (and optionally create) missing patient sessions for confirmed appointments')]
class BackfillAppointmentSessionsCommand extends Command
{
    private const FILED_STATUSES = [
        Appointment::STATUS_CONFIRMED,
        Appointment::STATUS_COMPLETED,
    ];

    public function __construct(
        private readonly AppointmentRepository          $appointmentRepo,
        private readonly PatientSessionRepository       $sessionRepo,
        private readonly AppointmentConfirmationService $confirmation,
        private readonly EntityManagerInterface         $em,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this->addOption('fix', null, InputOption::VALUE_NONE, 'Create the missing sessions instead of only reporting them');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io  = new SymfonyStyle($input, $output);
        $fix = (bool) $input->getOption('fix');

        $appointments = $this->appointmentRepo->createQueryBuilder('a')
            ->where('a.status IN (:statuses)')
            ->andWhere('a.isReserve = false')
            ->setParameter('statuses', self::FILED_STATUSES)
            ->orderBy('a.id', 'ASC')
            ->getQuery()
            ->getResult();

        $rows    = [];
        $missing = [];

        foreach ($appointments as $appointment) {
            $clinic = $appointment->getClinic();
            [$entityType, $entityId] = $clinic !== null
                ? ['clinic', (int) $clinic->getId()]
                : ['doctor', (int) $appointment->getDoctor()->getId()];

            if ($this->sessionRepo->findByAppointmentAndEntity($appointment, $entityType, $entityId) !== null) {
                continue;
            }

            $missing[] = [$appointment, $entityType, $entityId];
            $rows[]    = [
                $appointment->getUuid(),
                $appointment->getStatus(),
                date('Y-m-d H:i', $appointment->getSlotStart()),
                $appointment->getDoctor()->getName(),
                $clinic !== null ? ($clinic->getName() ?? 'clinic') : 'personal',
            ];
        }

        if ($rows === []) {
            $io->success('Every confirmed appointment already has its patient session.');

            return Command::SUCCESS;
        }

        $io->table(['appointment', 'status', 'slot', 'doctor', 'context'], $rows);

        if (!$fix) {
            $io->warning(sprintf('%d appointment(s) without a session. Re-run with --fix to create them.', count($rows)));

            return Command::SUCCESS;
        }

        $created = 0;
        foreach ($missing as [$appointment, $entityType, $entityId]) {
            $this->confirmation->onConfirmed($appointment);
            $this->em->flush();

            if ($this->sessionRepo->findByAppointmentAndEntity($appointment, $entityType, $entityId) !== null) {
                $created++;
            }
        }

        // آن‌هایی که ساخته نشدند عمداً رد شده‌اند (نبودِ ویژگی patient_records برای آن
        // tenant)؛ جدا گزارش می‌شوند تا با شکست اشتباه گرفته نشوند.
        $skipped = count($missing) - $created;
        $io->success(sprintf('Created %d session(s).', $created));
        if ($skipped > 0) {
            $io->note(sprintf('%d skipped — their tenant has no patient_records feature.', $skipped));
        }

        return Command::SUCCESS;
    }
}
