<?php

namespace App\Appointment\Command;

use App\Appointment\Entity\WeeklySchedule;
use App\Appointment\Repository\WeeklyScheduleRepository;
use App\Doctor\Repository\DoctorAddressRepository;
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 active shifts whose location_id is empty or points outside their own
 * booking context.
 *
 * The API rejects such shifts today (validateSessions), but rows created before
 * that validation are still around. They are invisible in the panel yet make a
 * location look bookable when nothing can actually be reserved there — the
 * public site used to advertise a "personal practice" built entirely out of one.
 */
#[AsCommand(name: 'app:schedule:audit-locations', description: 'Report (and optionally disable) schedule shifts pointing at no valid address')]
class AuditScheduleLocationsCommand extends Command
{
    public function __construct(
        private readonly WeeklyScheduleRepository $scheduleRepo,
        private readonly DoctorAddressRepository  $addressRepo,
        private readonly EntityManagerInterface   $em,
    ) {
        parent::__construct();
    }

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

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

        $rows    = [];
        $touched = 0;

        foreach ($this->scheduleRepo->findAll() as $schedule) {
            $clinic  = $schedule->getClinic();
            $allowed = [];
            foreach ($this->addressRepo->findForContext($schedule->getDoctor(), $clinic?->getId()) as $address) {
                $allowed[(int) $address->getId()] = true;
            }

            $setting = $schedule->getSetting();
            $changed = false;

            foreach ($setting as $dayKey => $day) {
                foreach (($day['sessions'] ?? []) as $index => $session) {
                    if (!($session['active'] ?? false)) {
                        continue;
                    }

                    $locationId = (int) ($session['location_id'] ?? 0);
                    if ($locationId !== 0 && isset($allowed[$locationId])) {
                        continue;
                    }

                    $rows[] = [
                        $schedule->getDoctor()->getUuid(),
                        $clinic === null ? 'personal' : ($clinic->getName() ?? 'clinic'),
                        WeeklySchedule::DAYS[(int) $dayKey] ?? (string) $dayKey,
                        $locationId === 0 ? '—' : (string) $locationId,
                        $locationId === 0 ? 'no address' : 'address outside context',
                    ];

                    if ($fix) {
                        $setting[$dayKey]['sessions'][$index]['active'] = false;
                        $changed = true;
                    }
                }
            }

            if ($changed) {
                // داده‌ی کاربر حذف نمی‌شود؛ فقط غیرفعال می‌شود تا قابل بازیابی بماند.
                $schedule->setSetting($setting);
                $touched++;
            }
        }

        if ($rows === []) {
            $io->success('No schedule shift points at a missing or foreign address.');

            return Command::SUCCESS;
        }

        $io->table(['doctor', 'context', 'day', 'location_id', 'problem'], $rows);

        if (!$fix) {
            $io->warning(sprintf('%d offending shift(s). Re-run with --fix to deactivate them.', count($rows)));

            return Command::SUCCESS;
        }

        $this->em->flush();
        $io->success(sprintf('Deactivated %d shift(s) across %d schedule(s).', count($rows), $touched));

        return Command::SUCCESS;
    }
}
