<?php

namespace App\Doctor\Command;

use App\Doctor\Service\Repair\DoctorRepairStep;
use App\Doctor\Service\Repair\RepairOptions;
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;
use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;

/**
 * تنها کامند ترمیم دادهٔ ایمپورت‌شده از خزندهٔ نظام پزشکی.
 *
 * جای چهار کامند جداگانه را گرفته (fix-irimc-names، fix-irimc-degrees،
 * backfill-specialty-parents، backfill-surrogate-role) تا بعد از هر خزش
 * یک دستور کافی باشد. همهٔ گام‌ها idempotent‌اند؛ اجرای دوباره بی‌ضرر است.
 *
 *   php bin/console app:doctors:repair --dry-run          # گزارش کامل، بدون تغییر
 *   php bin/console app:doctors:repair                    # اعمال همهٔ گام‌ها
 *   php bin/console app:doctors:repair --only=degrees     # فقط یک گام
 *   php bin/console app:doctors:repair --skip=names       # همه جز یک گام
 *   php bin/console app:doctors:repair --list             # فهرست گام‌ها
 *
 * گام جدید = یک کلاس با DoctorRepairStep در src/Doctor/Service/Repair/؛
 * خودکار پیدا و اجرا می‌شود و نیازی به دست‌زدن به این کامند نیست.
 */
#[AsCommand(
    name: 'app:doctors:repair',
    description: 'Repair crawler-imported doctor data — names, degrees, specialty parents, surrogate roles (idempotent, supports --dry-run)',
)]
class RepairImportedDoctorsCommand extends Command
{
    /** @var DoctorRepairStep[] */
    private readonly array $steps;

    public function __construct(
        private readonly EntityManagerInterface $em,
        #[AutowireIterator('app.doctor_repair_step')] iterable $steps,
    ) {
        $this->steps = iterator_to_array($steps, false);
        parent::__construct();
    }

    protected function configure(): void
    {
        $this
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Report only, change nothing')
            ->addOption('only', null, InputOption::VALUE_REQUIRED, 'Run only these steps (comma-separated)')
            ->addOption('skip', null, InputOption::VALUE_REQUIRED, 'Run everything except these steps (comma-separated)')
            ->addOption('all-sources', null, InputOption::VALUE_NONE, "Every doctor, not just source='irimc'")
            ->addOption('include-claimed', null, InputOption::VALUE_NONE, 'Also overwrite claimed profiles (owner input normally wins)')
            ->addOption('list', null, InputOption::VALUE_NONE, 'List the available steps and exit');
    }

    /** @return string[] */
    private function parseList(?string $raw): array
    {
        if ($raw === null || trim($raw) === '') {
            return [];
        }

        return array_values(array_filter(array_map('trim', explode(',', $raw))));
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);

        if ($input->getOption('list')) {
            $io->table(
                ['گام', 'توضیح'],
                array_map(fn (DoctorRepairStep $s) => [$s->name(), $s->description()], $this->steps),
            );

            return Command::SUCCESS;
        }

        $only = $this->parseList($input->getOption('only'));
        $skip = $this->parseList($input->getOption('skip'));

        $known = array_map(fn (DoctorRepairStep $s) => $s->name(), $this->steps);
        foreach ([...$only, ...$skip] as $requested) {
            if (!in_array($requested, $known, true)) {
                $io->error(sprintf('گام ناشناخته «%s». گام‌های موجود: %s', $requested, implode(', ', $known)));

                return Command::INVALID;
            }
        }

        $selected = array_filter($this->steps, function (DoctorRepairStep $s) use ($only, $skip) {
            if ($only !== [] && !in_array($s->name(), $only, true)) {
                return false;
            }

            return !in_array($s->name(), $skip, true);
        });

        if ($selected === []) {
            $io->warning('هیچ گامی برای اجرا انتخاب نشد.');

            return Command::SUCCESS;
        }

        $options = new RepairOptions(
            dryRun: (bool) $input->getOption('dry-run'),
            allSources: (bool) $input->getOption('all-sources'),
            includeClaimed: (bool) $input->getOption('include-claimed'),
        );

        if ($options->dryRun) {
            $io->note('حالت dry-run — هیچ تغییری نوشته نمی‌شود.');
        }

        $rows         = [];
        $totalChanged = 0;
        foreach ($selected as $step) {
            $io->section($step->description());
            $result = $step->run($options, $io);

            $totalChanged += $result->changed;
            $rows[] = [
                $step->name(),
                $result->scanned,
                $result->changed,
                $result->note ?? ($result->changed === 0 ? 'بدون تغییر' : ''),
            ];
        }

        // یک flush در پایان: کل ترمیم یک تراکنش است و dry-run هرگز چیزی نمی‌نویسد.
        if (!$options->dryRun && $totalChanged > 0) {
            $this->em->flush();
        }

        $io->newLine();
        $io->table(['گام', 'بررسی‌شده', $options->dryRun ? 'قابل اصلاح' : 'اصلاح‌شده', 'توضیح'], $rows);

        $io->success(sprintf(
            'مجموعاً %d رکورد %s در %d گام.',
            $totalChanged,
            $options->dryRun ? 'قابل اصلاح است' : 'اصلاح شد',
            count($selected),
        ));

        return Command::SUCCESS;
    }
}
