<?php

namespace App\Tests\Doctor;

use App\Doctor\Entity\Doctor;
use App\Doctor\Service\Repair\DoctorRepairStep;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;

/**
 * `app:doctors:repair` جای چهار کامند ترمیمی جداگانه را گرفته است. این تست سه
 * قرارداد را قفل می‌کند: گام‌ها خودکار کشف می‌شوند، --dry-run هیچ‌چیز نمی‌نویسد،
 * و اجرای دوم صفر تغییر می‌دهد (idempotent).
 */
class RepairImportedDoctorsCommandTest extends KernelTestCase
{
    private function tester(): CommandTester
    {
        return new CommandTester(
            (new Application(self::bootKernel()))->find('app:doctors:repair')
        );
    }

    /** پزشک irimc با نام عنوان‌دار و درجهٔ جابه‌جا — دقیقاً شکل رکوردهای معیوب. */
    private function makeBrokenDoctor(): Doctor
    {
        $em     = static::getContainer()->get('doctrine')->getManager();
        $mobile = '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT);

        $user = new \App\Auth\Entity\User($mobile);
        $user->setStatus(0);
        $em->persist($user);

        $doctor = new Doctor($user, 'دکتر آزمون ترمیم');
        $doctor->setSource('irimc');
        $doctor->setOwnerStatus('unclaimed');
        $doctor->setInfo('تخصص بیماری‌های داخلی دکترای حرفه‌ای پزشکی');
        $doctor->setDegree('expert');   // غلط: باید specialist باشد
        $em->persist($doctor);
        $em->flush();

        return $doctor;
    }

    public function testEveryStepIsAutoDiscovered(): void
    {
        self::bootKernel();

        try {
            $steps = iterator_to_array(
                static::getContainer()->get('test.doctor_repair_steps'),
                false
            );
        } catch (ServiceNotFoundException) {
            // بدون alias عمومی برای تگ، از خروجی --list به‌عنوان منبع حقیقت استفاده کن.
            $tester = $this->tester();
            $tester->execute(['--list' => true]);
            $out = $tester->getDisplay();

            foreach (['names', 'degrees', 'specialty-parents', 'surrogate-role'] as $name) {
                $this->assertStringContainsString($name, $out, "گام «$name» در --list نیست");
            }

            return;
        }

        $names = array_map(fn (DoctorRepairStep $s) => $s->name(), $steps);
        foreach (['names', 'degrees', 'specialty-parents', 'surrogate-role'] as $name) {
            $this->assertContains($name, $names);
        }
    }

    public function testDryRunWritesNothing(): void
    {
        $doctor = $this->makeBrokenDoctor();
        $em     = static::getContainer()->get('doctrine')->getManager();
        $id     = $doctor->getId();

        $tester = $this->tester();
        $tester->execute(['--dry-run' => true, '--only' => 'degrees,names']);
        $tester->assertCommandIsSuccessful();

        $em->clear();
        $fresh = $em->getRepository(Doctor::class)->find($id);

        $this->assertSame('expert', $fresh->getDegree(), 'dry-run نباید درجه را عوض کند');
        $this->assertSame('دکتر آزمون ترمیم', $fresh->getName(), 'dry-run نباید نام را عوض کند');
    }

    public function testRepairFixesDegreeAndNameThenIsIdempotent(): void
    {
        $doctor = $this->makeBrokenDoctor();
        $em     = static::getContainer()->get('doctrine')->getManager();
        $id     = $doctor->getId();

        $this->tester()->execute(['--only' => 'degrees,names']);

        $em->clear();
        $fresh = $em->getRepository(Doctor::class)->find($id);
        $this->assertSame('specialist', $fresh->getDegree(), 'درجه باید از عنوان خام بازمحاسبه شود');
        $this->assertSame('آزمون ترمیم', $fresh->getName(), 'پیشوند «دکتر» باید حذف شود');

        // اجرای دوم: همان رکورد دیگر نباید در خروجی بیاید.
        $second = $this->tester();
        $second->execute(['--only' => 'degrees,names']);
        $this->assertStringNotContainsString("#$id ", $second->getDisplay(), 'گام باید idempotent باشد');
    }

    public function testUnknownStepIsRejected(): void
    {
        $tester = $this->tester();
        $tester->execute(['--only' => 'nonsense']);

        $this->assertSame(2, $tester->getStatusCode(), 'گام ناشناخته باید INVALID برگرداند');
        $this->assertStringContainsString('گام ناشناخته', $tester->getDisplay());
    }
}
