<?php

namespace App\Tests\Doctor;

use App\Doctor\Entity\Doctor;
use App\Location\Entity\City;
use App\Location\Entity\Province;
use App\Tests\ApiTestCase;

/**
 * Regression contract for POST /api/v1/admin/doctors/import (IRIMC import).
 * Written BEFORE extracting the logic into DoctorImportService — the HTTP
 * contract (routes, statuses, {uuid, created, skipped} payload) must not change.
 */
class DoctorImportTest extends ApiTestCase
{
    private function importPayload(string $code): array
    {
        return [
            'name'                => 'دکتر صفورا حجازی نیا',
            'medical_system_code' => $code,
            'source_ref'          => 'https://membersearch.irimc.org/member/profile?id=test',
            'gender'              => 'man',
            'degree'              => 'general',
            'info'                => 'دکترای حرفه‌ای پزشکی',
        ];
    }

    /** db_test is never reset — randomise the natural key per run. */
    private function freshCode(): string
    {
        return 'T' . random_int(100_000, 999_999) . random_int(100, 999);
    }

    public function testImportCreatesUnclaimedDoctorWithSurrogateUser(): void
    {
        $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
        $code  = $this->freshCode();

        $data = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $this->importPayload($code));

        $this->assertSame(201, $this->responseCode());
        $this->assertTrue($data['success']);
        $this->assertTrue($data['data']['created']);
        $this->assertNotEmpty($data['data']['uuid']);

        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $data['data']['uuid']]);
        $this->assertSame('unclaimed', $doctor->getOwnerStatus());
        $this->assertSame('irimc', $doctor->getSource());
        $this->assertTrue($doctor->isActiveDoctorAppointment());
        // نام بدون پیشوند «دکتر» ذخیره می‌شود (UI خودش «دکتر» را جلو می‌گذارد)
        $this->assertSame('صفورا حجازی نیا', $doctor->getName());

        $surrogate = $doctor->getUser();
        $this->assertStringStartsWith('imp_', $surrogate->getMobileNumber());
        $this->assertSame(0, $surrogate->getStatus());
        $this->assertTrue($surrogate->hasRole('ROLE_UNCLAIMED_DOCTOR'));
    }

    public function testReimportUpdatesInsteadOfDuplicating(): void
    {
        $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
        $code  = $this->freshCode();

        $first = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $this->importPayload($code));
        $this->assertSame(201, $this->responseCode());

        $payload         = $this->importPayload($code);
        $payload['name'] = 'دکتر تست ویرایش‌شده';
        $second          = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $payload);

        $this->assertSame(200, $this->responseCode());
        $this->assertFalse($second['data']['created']);
        $this->assertSame($first['data']['uuid'], $second['data']['uuid']);

        $count = $this->em->getRepository(Doctor::class)->count(['source' => 'irimc', 'medicalSystemCode' => $code]);
        $this->assertSame(1, $count);

        $this->em->clear();
        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $first['data']['uuid']]);
        // نیم‌فاصله در normalize به فاصله تبدیل می‌شود
        $this->assertSame('تست ویرایش شده', $doctor->getName());
    }

    public function testClaimedDoctorIsNeverOverwritten(): void
    {
        $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
        $code  = $this->freshCode();

        $created = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $this->importPayload($code));
        $uuid    = $created['data']['uuid'];

        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]);
        $owner  = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
        $doctor->transferOwnershipTo($owner);
        $this->em->flush();
        $originalName = $doctor->getName();

        $payload         = $this->importPayload($code);
        $payload['name'] = 'دکتر بازنویسی ممنوع';
        $reimport        = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $payload);

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

        $this->em->clear();
        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $uuid]);
        $this->assertSame($originalName, $doctor->getName());
        $this->assertSame('claimed', $doctor->getOwnerStatus());
    }

    public function testImportCreatesDefaultOfficeAddressWithCityAndProvince(): void
    {
        $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
        $code  = $this->freshCode();

        $province = new Province('استان تست ' . $code);
        $city     = new City('شهر تست ' . $code, $province);
        $this->em->persist($province);
        $this->em->persist($city);
        $this->em->flush();

        $payload           = $this->importPayload($code);
        $payload['states'] = [$province->getId()];
        $payload['cities'] = [$city->getId()];

        $data = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $payload);
        $this->assertSame(201, $this->responseCode());

        $cityId     = $city->getId();
        $provinceId = $province->getId();
        $this->em->clear();
        $doctor  = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $data['data']['uuid']]);
        $this->assertCount(1, $doctor->getAddresses());
        $address = $doctor->getAddresses()->first();
        $this->assertSame('مطب دکتر صفورا حجازی نیا', $address->getName());
        $this->assertSame($cityId, $address->getCity()?->getId());
        $this->assertSame($provinceId, $address->getProvince()?->getId());

        // ایمپورت مجدد نباید آدرس تکراری بسازد یا نام آدرس را بازنویسی کند
        $address->setName('مطب ویرایش‌شده توسط کاربر');
        $this->em->flush();

        $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $payload);
        $this->assertSame(200, $this->responseCode());

        $this->em->clear();
        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $data['data']['uuid']]);
        $this->assertCount(1, $doctor->getAddresses());
        $this->assertSame('مطب ویرایش‌شده توسط کاربر', $doctor->getAddresses()->first()->getName());
    }

    public function testImportWithoutLocationStillCreatesOfficeAddress(): void
    {
        $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
        $code  = $this->freshCode();

        $data = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $this->importPayload($code));
        $this->assertSame(201, $this->responseCode());

        $this->em->clear();
        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['uuid' => $data['data']['uuid']]);
        $this->assertCount(1, $doctor->getAddresses());
        $this->assertSame('مطب دکتر صفورا حجازی نیا', $doctor->getAddresses()->first()->getName());
    }

    public function testManualDoctorWithSameCodeIsNeverDuplicated(): void
    {
        $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);
        $code  = $this->freshCode();

        $owner  = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
        $manual = new Doctor($owner, 'پزشک دستی');
        $manual->setMedicalSystemCode($code);
        $this->em->persist($manual);
        $this->em->flush();

        $data = $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, $this->importPayload($code));

        $this->assertSame(200, $this->responseCode());
        $this->assertFalse($data['data']['created']);
        $this->assertSame('duplicate', $data['data']['skipped']);
        $this->assertSame($manual->getUuid(), $data['data']['uuid']);

        $this->em->clear();
        $count = $this->em->getRepository(Doctor::class)->count(['medicalSystemCode' => $code]);
        $this->assertSame(1, $count);
        $doctor = $this->em->getRepository(Doctor::class)->findOneBy(['medicalSystemCode' => $code]);
        $this->assertSame('پزشک دستی', $doctor->getName());
        $this->assertSame('manual', $doctor->getSource());
    }

    public function testValidationErrors(): void
    {
        $admin = $this->createUser(['ROLE_USER', 'ROLE_ADMIN']);

        $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, ['medical_system_code' => $this->freshCode()]);
        $this->assertSame(422, $this->responseCode());

        $this->authJson('POST', '/api/v1/admin/doctors/import', $admin, ['name' => 'دکتر بی‌کد']);
        $this->assertSame(422, $this->responseCode());
    }

    public function testNonAdminIsRejected(): void
    {
        $user = $this->createUser(['ROLE_USER']);
        $this->authJson('POST', '/api/v1/admin/doctors/import', $user, $this->importPayload($this->freshCode()));
        $this->assertSame(403, $this->responseCode());
    }

    public function testImporterRoleCanImportButNothingElse(): void
    {
        $importer = $this->createUser(['ROLE_USER', 'ROLE_IMPORTER']);

        $this->authJson('POST', '/api/v1/admin/doctors/import', $importer, $this->importPayload($this->freshCode()));
        $this->assertSame(201, $this->responseCode(), 'ROLE_IMPORTER must be able to import');

        $this->authJson('GET', '/api/v1/admin/users', $importer);
        $this->assertSame(403, $this->responseCode(), 'ROLE_IMPORTER must NOT reach other admin endpoints');

        $this->authJson('GET', '/api/v1/admin/doctor-claims', $importer);
        $this->assertSame(403, $this->responseCode());
    }
}
