<?php

namespace App\Tests\Shared;

use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Tests\ApiTestCase;
use PHPUnit\Framework\Attributes\DataProvider;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;

/**
 * `app:tenant:dump` مقدارهایش را مستقیم داخل شرط --where و خط فرمان می‌گذارد، پس
 * اعتبارسنجی ورودی اینجا امنیتی است نه سلیقه‌ای.
 */
class TenantDumpCommandTest extends ApiTestCase
{
    private function tester(): CommandTester
    {
        $application = new Application(static::$kernel);

        return new CommandTester($application->find('app:tenant:dump'));
    }

    private function outputPath(): string
    {
        return sys_get_temp_dir() . '/tenant-dump-test-' . bin2hex(random_bytes(8)) . '.sql';
    }

    public function testDumpsOnlyTheRequestedClinicRows(): void
    {
        $clinic = new Clinic($this->createUser(['ROLE_CLINIC']));
        $clinic->setName('کلینیک بکاپ');
        $this->em->persist($clinic);
        $this->em->flush();

        $path   = $this->outputPath();
        $tester = $this->tester();
        $exit   = $tester->execute(['--tenant' => 'clinic:' . $clinic->getId(), '--output' => $path]);

        self::assertSame(0, $exit, $tester->getDisplay());
        self::assertFileExists($path);

        $sql = (string) file_get_contents($path);
        self::assertStringNotContainsString("'doctor',", $sql, 'ردیف محیط دیگری نباید در خروجی باشد');

        unlink($path);
    }

    /** ⚠️ محیط بدون داده: فایل معتبر، بدون خطا. */
    public function testTenantWithNoRowsStillProducesAValidFile(): void
    {
        $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر بی‌داده');
        $this->em->persist($doctor);
        $this->em->flush();

        $path   = $this->outputPath();
        $tester = $this->tester();

        self::assertSame(0, $tester->execute(['--tenant' => 'doctor:' . $doctor->getId(), '--output' => $path]));
        self::assertFileExists($path);

        unlink($path);
    }

    public function testUnknownTenantFails(): void
    {
        $path   = $this->outputPath();
        $tester = $this->tester();

        self::assertSame(1, $tester->execute(['--tenant' => 'clinic:99999999', '--output' => $path]));
        self::assertStringContainsString('وجود ندارد', $tester->getDisplay());
    }

    #[DataProvider('malformedTenants')]
    public function testMalformedTenantIsRejectedBeforeTouchingTheDatabase(string $tenant): void
    {
        $tester = $this->tester();

        self::assertSame(1, $tester->execute(['--tenant' => $tenant, '--output' => $this->outputPath()]));
        self::assertStringContainsString('doctor:<id>', $tester->getDisplay());
    }

    /** @return iterable<string, array{string}> */
    public static function malformedTenants(): iterable
    {
        yield 'sql injection'   => ['clinic:1 OR 1=1'];
        yield 'shell injection' => ['clinic:1; rm -rf /'];
        yield 'unknown type'    => ['admin:1'];
        yield 'zero id'         => ['clinic:0'];
        yield 'negative id'     => ['clinic:-1'];
        yield 'missing id'      => ['clinic'];
        yield 'empty'           => [''];
    }

    public function testOutputOptionIsRequired(): void
    {
        $tester = $this->tester();

        self::assertSame(1, $tester->execute(['--tenant' => 'clinic:1']));
        self::assertStringContainsString('--output', $tester->getDisplay());
    }
}
