<?php

namespace App\Tests\Doctor;

use App\Auth\Entity\User;
use App\Doctor\Entity\Doctor;
use App\Doctor\Entity\DoctorClaimRequest;
use App\Shared\Service\ApiIrService;
use App\Tests\ApiTestCase;

/**
 * جریان تصاحب پروفایل پزشک ایمپورت‌شده. ApiIrService همیشه mock می‌شود —
 * تست‌ها هرگز به سرویس واقعی api.ir درخواست نمی‌زنند.
 */
class DoctorClaimTest extends ApiTestCase
{
    protected function setUp(): void
    {
        parent::setUp();
        $this->client->disableReboot();
    }

    private function mockApiIr(bool $shahkar = true, ?array $person = ['firstName' => 'تست', 'lastName' => 'ایمپورت', 'alive' => true]): void
    {
        $mock = $this->createMock(ApiIrService::class);
        $mock->method('isConfigured')->willReturn(true);
        $mock->method('shahkarMatch')->willReturn($shahkar);
        $mock->method('personInfo')->willReturn($person);
        static::getContainer()->set(ApiIrService::class, $mock);
    }

    private function importUnclaimedDoctor(): string
    {
        $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
        $code  = 'C' . random_int(100_000, 999_999) . random_int(100, 999);
        $data  = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, [
            'name'                => 'دکتر تست ایمپورت',
            'medical_system_code' => $code,
        ]);

        return $data['data']['uuid'];
    }

    private function claimBody(): array
    {
        // db_test هرگز reset نمی‌شود و users.national_code یکتاست → کد ملی هر تست تصادفی
        return [
            'national_code' => '00' . str_pad((string) random_int(0, 99_999_999), 8, '0', STR_PAD_LEFT),
            'birth_date'    => '1371/1/1',
            'first_name'    => 'تست',
            'last_name'     => 'ایمپورت',
        ];
    }

    public function testSuccessfulClaimTransfersOwnershipAndDeletesSurrogate(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $this->mockApiIr();

        $doctor       = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]);
        $surrogateId  = $doctor->getUser()->getId();
        $claimer      = $this->createUser(['ROLE_USER']);

        $res = $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, $this->claimBody());

        $this->assertSame(200, $this->responseCode());
        $this->assertSame('claimed', $res['data']['status']);

        $this->em->clear();
        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]);
        $this->assertSame('claimed', $doctor->getOwnerStatus());
        $this->assertSame($claimer->getId(), $doctor->getUser()->getId());
        $this->assertNull($doctor->getManagedBy());

        $claimer = $this->em->getRepository(User::class)->find($claimer->getId());
        $this->assertTrue($claimer->hasRole('ROLE_DOCTOR'));
        $this->assertTrue($claimer->isNationalCodeVerified());

        $this->assertNull($this->em->getRepository(User::class)->find($surrogateId), 'surrogate must be deleted');

        $claim = $this->em->getRepository(DoctorClaimRequest::class)->findOneBy(['doctor' => $doctor]);
        $this->assertSame(DoctorClaimRequest::STATUS_COMPLETED, $claim->getStatus());
    }

    public function testNameMismatchRevertsToUnclaimed(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $this->mockApiIr(person: ['firstName' => 'کس', 'lastName' => 'دیگری', 'alive' => true]);

        $claimer = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, $this->claimBody());

        $this->assertSame(422, $this->responseCode());

        $this->em->clear();
        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]);
        $this->assertSame('unclaimed', $doctor->getOwnerStatus(), 'must be free for the real doctor to retry');
        $this->assertStringStartsWith('imp_', $doctor->getUser()->getMobileNumber(), 'surrogate must survive');

        $claim = $this->em->getRepository(DoctorClaimRequest::class)->findOneBy(['doctor' => $doctor]);
        $this->assertSame(DoctorClaimRequest::STATUS_FAILED, $claim->getStatus());
        $this->assertNotNull($claim->getFailureReason());
    }

    public function testDeceasedPersonIsRejected(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $this->mockApiIr(person: ['firstName' => 'تست', 'lastName' => 'ایمپورت', 'alive' => false]);

        $claimer = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, $this->claimBody());

        $this->assertSame(422, $this->responseCode());
    }

    public function testShahkarMismatchIsRejected(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $this->mockApiIr(shahkar: false);

        $claimer = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, $this->claimBody());

        $this->assertSame(422, $this->responseCode());
    }

    public function testAlreadyClaimedIsConflict(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $this->mockApiIr();

        $first = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $first, $this->claimBody());
        $this->assertSame(200, $this->responseCode());

        $second = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $second, $this->claimBody());
        $this->assertSame(409, $this->responseCode());
    }

    public function testUserWhoAlreadyOwnsADoctorCannotClaim(): void
    {
        $uuidA = $this->importUnclaimedDoctor();
        $uuidB = $this->importUnclaimedDoctor();
        $this->mockApiIr();

        $claimer = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', "/api/v1/doctor/{$uuidA}/claim", $claimer, $this->claimBody());
        $this->assertSame(200, $this->responseCode());

        $this->authJson('POST', "/api/v1/doctor/{$uuidB}/claim", $claimer, $this->claimBody());
        $this->assertSame(409, $this->responseCode());

        $this->em->clear();
        $doctorB = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuidB]);
        $this->assertSame('unclaimed', $doctorB->getOwnerStatus());
    }

    public function testValidationErrors(): void
    {
        $uuid    = $this->importUnclaimedDoctor();
        $this->mockApiIr();
        $claimer = $this->createUser(['ROLE_USER']);

        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, ['national_code' => '123', 'birth_date' => '1371/1/1', 'first_name' => 'الف', 'last_name' => 'ب']);
        $this->assertSame(422, $this->responseCode());

        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, ['national_code' => '0010007700', 'birth_date' => 'invalid', 'first_name' => 'الف', 'last_name' => 'ب']);
        $this->assertSame(422, $this->responseCode());
    }

    public function testMobileMismatchIsRejected(): void
    {
        $uuid    = $this->importUnclaimedDoctor();
        $this->mockApiIr();
        $claimer = $this->createUser(['ROLE_USER']);

        $body = $this->claimBody();
        $body['mobile'] = '09990000000'; // متفاوت با موبایل کاربر
        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, $body);
        $this->assertSame(422, $this->responseCode());
    }

    public function testOwnerCanDeleteOwnProfileButOthersCannot(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $this->mockApiIr();
        $claimer = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', "/api/v1/doctor/{$uuid}/claim", $claimer, $this->claimBody());
        $this->assertSame(200, $this->responseCode());

        // کاربر دیگر → 403
        $other = $this->createUser(['ROLE_USER']);
        $this->authJson('DELETE', "/api/v1/doctor/{$uuid}", $other);
        $this->assertSame(403, $this->responseCode());

        // مالک → 200
        $this->authJson('DELETE', "/api/v1/doctor/{$uuid}", $claimer);
        $this->assertSame(200, $this->responseCode());

        $this->em->clear();
        $this->assertNull($this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]));
    }

    public function testUnclaimedProfileNotDeletableByRandomUser(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $user = $this->createUser(['ROLE_USER']);
        $this->authJson('DELETE', "/api/v1/doctor/{$uuid}", $user);
        $this->assertSame(403, $this->responseCode());
    }

    public function testClaimInfoIsPublic(): void
    {
        $uuid = $this->importUnclaimedDoctor();

        $this->client->request('GET', "/api/v1/doctor/{$uuid}/claim-info");
        $this->assertSame(200, $this->responseCode());
        $data = json_decode($this->client->getResponse()->getContent(), true);
        $this->assertTrue($data['data']['claimable']);
        $this->assertSame('unclaimed', $data['data']['owner_status']);
    }

    public function testAdminTransferHappyPath(): void
    {
        $uuid   = $this->importUnclaimedDoctor();
        $admin  = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
        $mobile = '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT);

        $res = $this->authJson('POST', "/api/v1/admin/doctors/{$uuid}/transfer", $admin, ['mobile' => $mobile]);

        $this->assertSame(200, $this->responseCode());
        $this->assertSame('claimed', $res['data']['owner_status']);

        $this->em->clear();
        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]);
        $this->assertSame('claimed', $doctor->getOwnerStatus());
        $this->assertSame($mobile, $doctor->getUser()->getMobileNumber());
        $this->assertTrue($doctor->getUser()->hasRole('ROLE_DOCTOR'));

        // transfer دوباره → 409
        $this->authJson('POST', "/api/v1/admin/doctors/{$uuid}/transfer", $admin, ['mobile' => $mobile]);
        $this->assertSame(409, $this->responseCode());
    }

    public function testAdminTransferRequiresAdmin(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $user = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', "/api/v1/admin/doctors/{$uuid}/transfer", $user, ['mobile' => '09121234567']);
        $this->assertSame(403, $this->responseCode());
    }

    public function testClaimRequiresAuthentication(): void
    {
        $uuid = $this->importUnclaimedDoctor();
        $this->client->request('POST', "/api/v1/doctor/{$uuid}/claim", server: ['CONTENT_TYPE' => 'application/json'], content: json_encode($this->claimBody()));
        $this->assertSame(401, $this->responseCode());
    }
}
