<?php

namespace App\Appointment\Command;

use App\Appointment\Entity\Appointment;
use App\Appointment\Service\ServiceBookingCalculator;
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;

/**
 * نوبت‌های **آیندهٔ** محیط‌های سرویسی که پیش از افزودن ستون‌های
 * `service_total_minutes` / `service_buffer_minutes` ثبت شده‌اند.
 *
 * مقدار از **خودِ نوبت** گرفته می‌شود (`slot_end - slot_start`)، نه از بازمحاسبهٔ مدت
 * سرویس‌ها: نوبت موجود ممکن است با مدت دستی ثبت شده باشد و بازمحاسبه یعنی تغییرِ
 * گذشته — همان چیزی که قانون پنجم مستند ممنوع کرده.
 *
 * نوبت‌های اسلاتی و نوبت‌های گذشته رد می‌شوند. نوبت رزرو (`slot_start == slot_end`) هم
 * مدتی برای استنتاج ندارد و رد می‌شود.
 *
 * dry-run پیش‌فرض است؛ با `--force` می‌نویسد. اجرای دوباره چیزی را دوباره ست نمی‌کند.
 */
#[AsCommand(
    name: 'app:appointment:backfill-service-duration',
    description: 'Fill service_total_minutes/service_buffer_minutes for future service-mode appointments',
)]
class BackfillServiceDurationCommand extends Command
{
    /** فقط نوبت‌هایی که هنوز اتفاق نیفتاده‌اند؛ گذشته را بازنویسی نمی‌کنیم. */
    private const LIVE_STATUSES = [
        Appointment::STATUS_PENDING,
        Appointment::STATUS_CONFIRMED,
    ];

    public function __construct(
        private readonly EntityManagerInterface   $em,
        private readonly ServiceBookingCalculator $calculator,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this->addOption('force', null, InputOption::VALUE_NONE, 'Write the values instead of only reporting them');
    }

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

        /** @var Appointment[] $candidates */
        $candidates = $this->em->createQueryBuilder()
            ->select('a')
            ->from(Appointment::class, 'a')
            ->where('a.serviceTotalMinutes IS NULL')
            ->andWhere('a.status IN (:live)')
            ->andWhere('a.slotStart > :now')
            ->andWhere('a.isReserve = false')
            ->setParameter('live', self::LIVE_STATUSES)
            ->setParameter('now', time())
            ->orderBy('a.slotStart', 'ASC')
            ->getQuery()
            ->getResult();

        $rows    = [];
        $skipped = 0;

        foreach ($candidates as $appointment) {
            if (!$this->calculator->isServiceMode($appointment->getDoctor(), $appointment->getClinic())) {
                $skipped++;
                continue;
            }

            $minutes = intdiv($appointment->getSlotEnd() - $appointment->getSlotStart(), 60);
            if ($minutes <= 0) {
                $skipped++;
                continue;
            }

            $buffer = $this->calculator->bufferMinutes($appointment->getDoctor(), $appointment->getClinic());
            $rows[] = [
                $appointment->getUuid(),
                date('Y-m-d H:i', $appointment->getSlotStart()),
                $minutes,
                $buffer,
            ];

            if ($force) {
                $appointment->setServiceDuration($minutes, $buffer);
            }
        }

        if ($rows === []) {
            $io->success(sprintf('چیزی برای backfill نیست (%d نوبت غیرمرتبط رد شد).', $skipped));

            return Command::SUCCESS;
        }

        $io->table(['uuid', 'شروع', 'مدت (دقیقه)', 'بافر (دقیقه)'], $rows);

        if (!$force) {
            $io->warning(sprintf(
                '%d نوبت قابل backfill است. برای نوشتن، دوباره با --force اجرا کنید.',
                count($rows),
            ));

            return Command::SUCCESS;
        }

        $this->em->flush();
        $io->success(sprintf('%d نوبت به‌روزرسانی شد (%d رد شد).', count($rows), $skipped));

        return Command::SUCCESS;
    }
}
