<?php

namespace App\Resource\Command;

use App\Appointment\Entity\WeeklySchedule;
use App\Resource\Entity\ClinicResource;
use App\Resource\Entity\ResourceCalendar;
use App\Resource\Repository\ClinicResourceRepository;
use App\Resource\Repository\ResourceCalendarRepository;
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;

/**
 * شیفت منابعِ پزشک را از `WeeklySchedule` موجود می‌سازد.
 *
 * بدون این، منابعی که تسک ۰۲ ساخته هیچ شیفتی ندارند و ساعت آزادشان همیشه خالی است —
 * یعنی لایهٔ منبع تا وقتی کسی دستی شیفت وارد نکند بی‌اثر می‌ماند.
 *
 * فقط منابعی که **هیچ** شیفتی ندارند پر می‌شوند: منبعی که کاربر خودش تنظیمش کرده
 * دست نمی‌خورد. همین idempotent بودن را هم تضمین می‌کند.
 *
 * برنامهٔ هفتگی دست‌نخورده می‌ماند؛ این کپی است نه مهاجرت.
 */
#[AsCommand(
    name: 'app:resource:calendar:backfill',
    description: 'Derive resource shifts from existing doctor weekly schedules',
)]
class BackfillResourceCalendarCommand extends Command
{
    public function __construct(
        private readonly EntityManagerInterface     $em,
        private readonly ClinicResourceRepository   $resources,
        private readonly ResourceCalendarRepository $calendars,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this->addOption('force', null, InputOption::VALUE_NONE, 'Actually write; without it the command only reports');
    }

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

        if (!$force) {
            $io->note('Dry run — nothing will be written. Re-run with --force to apply.');
        }

        $rows    = [];
        $skipped = 0;

        foreach ($this->em->getRepository(WeeklySchedule::class)->findAll() as $schedule) {
            $doctor = $schedule->getDoctor();

            foreach ($this->shiftsByLocation($schedule) as $locationId => $shifts) {
                $resource = $this->resourceFor($doctor, $locationId);

                if ($resource === null) {
                    continue;   // منبعی برای این آدرس ساخته نشده؛ اول app:resource:backfill
                }

                if ($this->calendars->findForResource($resource) !== []) {
                    $skipped++;
                    continue;   // کاربر خودش تنظیم کرده — دست نمی‌زنیم
                }

                $rows[] = [$resource->getName(), $resource->getAddress()->getName() ?? '—', count($shifts)];

                if ($force) {
                    foreach ($shifts as $shift) {
                        $this->em->persist(new ResourceCalendar(
                            $resource,
                            $shift['day'],
                            $shift['start'],
                            $shift['end'],
                            $shift['sequence'],
                        ));
                    }
                }
            }
        }

        if ($force) {
            $this->em->flush();
        }

        if ($rows !== []) {
            $io->table(['منبع', 'شعبه', 'تعداد شیفت'], $rows);
        }

        $io->success(sprintf(
            '%s — %d منبع · %d منبع از قبل شیفت داشت و دست‌نخورده ماند',
            $force ? 'ساخته شد' : 'ساخته می‌شود',
            count($rows),
            $skipped,
        ));

        return Command::SUCCESS;
    }

    private function resourceFor(\App\Doctor\Entity\Doctor $doctor, int $locationId): ?ClinicResource
    {
        $address = $this->em->getRepository(\App\Doctor\Entity\DoctorAddress::class)->find($locationId);

        return $address === null ? null : $this->resources->findForSubject($doctor, $address);
    }

    /**
     * @return array<int, list<array{day: int, start: int, end: int, sequence: int}>>
     *         شناسهٔ آدرس => شیفت‌ها
     */
    private function shiftsByLocation(WeeklySchedule $schedule): array
    {
        $byLocation = [];

        foreach ($schedule->getSetting() as $rawDay => $day) {
            if (!is_numeric($rawDay)) {
                continue;
            }

            $sequences = [];

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

                $locationId = $session['location_id'] ?? null;
                $start      = $this->toMinutes($session['start_time'] ?? null);
                $end        = $this->toMinutes($session['end_time'] ?? null);

                if (!is_numeric($locationId) || $start === null || $end === null || $end <= $start) {
                    continue;
                }

                $locationId = (int) $locationId;
                $sequences[$locationId] ??= 0;

                $byLocation[$locationId][] = [
                    'day'      => (int) $rawDay,
                    'start'    => $start,
                    'end'      => $end,
                    'sequence' => $sequences[$locationId]++,
                ];
            }
        }

        return $byLocation;
    }

    private function toMinutes(mixed $time): ?int
    {
        if (!is_string($time) || preg_match('/^(\d{1,2}):(\d{2})$/', $time, $m) !== 1) {
            return null;
        }

        $minutes = (int) $m[1] * 60 + (int) $m[2];

        return $minutes >= 0 && $minutes <= ResourceCalendar::MINUTES_IN_DAY ? $minutes : null;
    }
}
