<?php

namespace App\Tests\Admin;

use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Tests\ApiTestCase;

/**
 * مدیر کل می‌تواند شمارهٔ **ورود** پزشک/کلینیک را عوض کند. شماره هویتِ ورود است، پس
 * یکتا بودنش کنترل می‌شود و شمارهٔ نمایشیِ پزشک هم با آن هم‌گام می‌ماند.
 */
class AdminChangeLoginMobileTest extends ApiTestCase
{
    private function newMobile(): string
    {
        return '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT);
    }

    private function makeDoctor(): Doctor
    {
        $user   = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
        $doctor = new Doctor($user, 'دکتر تغییر شماره');
        $doctor->setMobileNumber($user->getMobileNumber());
        $this->em->persist($doctor);
        $this->em->flush();

        return $doctor;
    }

    private function makeClinic(): Clinic
    {
        $clinic = new Clinic($this->createUser(['ROLE_USER', 'ROLE_CLINIC']));
        $clinic->setName('کلینیک تغییر شماره');
        $this->em->persist($clinic);
        $this->em->flush();

        return $clinic;
    }

    private function reloadUser(int $id): User
    {
        $this->em->clear();

        return $this->em->getRepository(User::class)->find($id);
    }

    public function testAdminChangesDoctorLoginMobile(): void
    {
        $admin  = $this->createUser(['ROLE_ADMIN']);
        $doctor = $this->makeDoctor();
        $userId = $doctor->getUser()->getId();
        $mobile = $this->newMobile();

        $body = $this->authJson('PATCH', '/api/v1/admin/doctors/' . $doctor->getUuid() . '/mobile', $admin, [
            'mobile_number' => $mobile,
        ]);

        self::assertSame(200, $this->responseCode());
        self::assertSame($mobile, $body['data']['data']['mobile_number']);
        self::assertSame($mobile, $this->reloadUser($userId)->getMobileNumber());
    }

    /** شمارهٔ نمایشیِ پزشک وقتی با شمارهٔ ورود یکی بوده باید همراهش به‌روز شود. */
    public function testDoctorDisplayMobileFollowsTheLoginMobile(): void
    {
        $admin  = $this->createUser(['ROLE_ADMIN']);
        $doctor = $this->makeDoctor();
        $id     = $doctor->getId();
        $mobile = $this->newMobile();

        $this->authJson('PATCH', '/api/v1/admin/doctors/' . $doctor->getUuid() . '/mobile', $admin, [
            'mobile_number' => $mobile,
        ]);
        $this->em->clear();

        self::assertSame($mobile, $this->em->getRepository(Doctor::class)->find($id)->getMobileNumber());
    }

    public function testAdminChangesClinicLoginMobile(): void
    {
        $admin  = $this->createUser(['ROLE_ADMIN']);
        $clinic = $this->makeClinic();
        $userId = $clinic->getUser()->getId();
        $mobile = $this->newMobile();

        $this->authJson('PATCH', '/api/v1/admin/clinic/' . $clinic->getUuid() . '/mobile', $admin, [
            'mobile_number' => $mobile,
        ]);

        self::assertSame(200, $this->responseCode());
        self::assertSame($mobile, $this->reloadUser($userId)->getMobileNumber());
    }

    public function testDuplicateMobileIsRejected(): void
    {
        $admin  = $this->createUser(['ROLE_ADMIN']);
        $doctor = $this->makeDoctor();
        $taken  = $this->createUser(['ROLE_USER'])->getMobileNumber();

        $this->authJson('PATCH', '/api/v1/admin/doctors/' . $doctor->getUuid() . '/mobile', $admin, [
            'mobile_number' => $taken,
        ]);

        self::assertSame(409, $this->responseCode());
    }

    public function testInvalidMobileIsRejected(): void
    {
        $admin  = $this->createUser(['ROLE_ADMIN']);
        $doctor = $this->makeDoctor();

        $this->authJson('PATCH', '/api/v1/admin/doctors/' . $doctor->getUuid() . '/mobile', $admin, [
            'mobile_number' => '12345',
        ]);

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

    public function testPersianDigitsAreAccepted(): void
    {
        $admin  = $this->createUser(['ROLE_ADMIN']);
        $doctor = $this->makeDoctor();
        $userId = $doctor->getUser()->getId();

        // db_test بین اجراها پاک نمی‌شود؛ شمارهٔ ثابت در اجرای دوم تکراری می‌شد.
        $latin   = $this->newMobile();
        $persian = strtr($latin, ['0' => '۰', '1' => '۱', '2' => '۲', '3' => '۳', '4' => '۴',
                                  '5' => '۵', '6' => '۶', '7' => '۷', '8' => '۸', '9' => '۹']);

        $this->authJson('PATCH', '/api/v1/admin/doctors/' . $doctor->getUuid() . '/mobile', $admin, [
            'mobile_number' => $persian,
        ]);

        self::assertSame(200, $this->responseCode());
        self::assertSame($latin, $this->reloadUser($userId)->getMobileNumber());
    }

    public function testNonAdminCannotChangeIt(): void
    {
        $doctor = $this->makeDoctor();

        $this->authJson('PATCH', '/api/v1/admin/doctors/' . $doctor->getUuid() . '/mobile', $doctor->getUser(), [
            'mobile_number' => $this->newMobile(),
        ]);

        self::assertSame(403, $this->responseCode());
    }

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

        $this->authJson('PATCH', '/api/v1/admin/doctors/00000000-0000-4000-8000-000000000000/mobile', $admin, [
            'mobile_number' => $this->newMobile(),
        ]);

        self::assertSame(404, $this->responseCode());
    }
}
