<?php

namespace App\Tests\Secretary;

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

/**
 * A clinic manager can assign one secretary to several of the clinic's doctors
 * at once (many DoctorSecretary rows for one secretary User) and later re-sync
 * that set. Doctors outside the clinic are rejected; foreign owners are 403.
 */
class ClinicSharedSecretaryTest extends ApiTestCase
{
    /** @return array{0: User, 1: Clinic, 2: Doctor[]} */
    private function makeClinicWithDoctors(int $count): array
    {
        $owner  = $this->createUser(['ROLE_CLINIC']);
        $clinic = new Clinic($owner);
        $this->em->persist($clinic);

        $doctors = [];
        for ($i = 0; $i < $count; $i++) {
            $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), "دکتر $i");
            $this->em->persist($doctor);
            $clinic->getDoctors()->add($doctor);
            $doctors[] = $doctor;
        }
        $this->em->flush();

        return [$owner, $clinic, $doctors];
    }

    private function mobile(): string
    {
        return '09' . str_pad((string) random_int(0, 999_999_999), 9, '0', STR_PAD_LEFT);
    }

    public function testClinicOwnerAssignsSecretaryToMultipleDoctors(): void
    {
        [$owner, , $doctors] = $this->makeClinicWithDoctors(3);

        $body = $this->authJson('POST', '/api/v1/secretary', $owner, [
            'mobile_number' => $this->mobile(),
            'name'          => 'منشی مشترک',
            'doctor_uuids'  => [$doctors[0]->getUuid(), $doctors[1]->getUuid()],
        ]);

        $this->assertSame(201, $this->responseCode());
        $this->assertCount(2, $body['data']['created']);
        $this->assertSame([], $body['data']['skipped_not_in_clinic']);
        $this->assertNotEmpty($body['data']['secretary_uuid']);
    }

    public function testSkipsDuplicatesOnReassign(): void
    {
        [$owner, , $doctors] = $this->makeClinicWithDoctors(2);
        $mobile = $this->mobile();
        $uuids  = [$doctors[0]->getUuid(), $doctors[1]->getUuid()];

        $this->authJson('POST', '/api/v1/secretary', $owner, ['mobile_number' => $mobile, 'doctor_uuids' => $uuids]);
        $body = $this->authJson('POST', '/api/v1/secretary', $owner, ['mobile_number' => $mobile, 'doctor_uuids' => $uuids]);

        $this->assertSame(201, $this->responseCode());
        $this->assertCount(0, $body['data']['created']);
        $this->assertCount(2, $body['data']['skipped_duplicate']);
    }

    public function testRejectsDoctorOutsideClinic(): void
    {
        [$owner, , $doctors] = $this->makeClinicWithDoctors(1);
        $foreignDoctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'خارج از کلینیک');
        $this->em->persist($foreignDoctor);
        $this->em->flush();

        $body = $this->authJson('POST', '/api/v1/secretary', $owner, [
            'mobile_number' => $this->mobile(),
            'doctor_uuids'  => [$doctors[0]->getUuid(), $foreignDoctor->getUuid()],
        ]);

        $this->assertSame(201, $this->responseCode());
        $this->assertCount(1, $body['data']['created']);
        $this->assertContains($foreignDoctor->getUuid(), $body['data']['skipped_not_in_clinic']);
    }

    public function testSyncAddsAndRemovesDoctors(): void
    {
        [$owner, $clinic, $doctors] = $this->makeClinicWithDoctors(3);
        $mobile = $this->mobile();

        $assigned = $this->authJson('POST', '/api/v1/secretary', $owner, [
            'mobile_number' => $mobile,
            'doctor_uuids'  => [$doctors[0]->getUuid(), $doctors[1]->getUuid()],
        ]);
        $secretaryUuid = $assigned['data']['secretary_uuid'];

        // sync to {doctor1, doctor2} → drop doctor0, add doctor2
        $body = $this->authJson('PUT', '/api/v1/secretaries/clinic/' . $clinic->getUuid() . '/doctors', $owner, [
            'secretary_uuid' => $secretaryUuid,
            'doctor_uuids'   => [$doctors[1]->getUuid(), $doctors[2]->getUuid()],
        ]);

        $this->assertSame(200, $this->responseCode());
        $this->assertSame(1, $body['data']['added']);   // doctor2
        $this->assertSame(1, $body['data']['removed']); // doctor0
    }

    public function testSyncCopiesProfileAndPermissionsToNewLinks(): void
    {
        [$owner, $clinic, $doctors] = $this->makeClinicWithDoctors(2);
        $mobile = $this->mobile();

        $assigned = $this->authJson('POST', '/api/v1/secretary', $owner, [
            'mobile_number' => $mobile,
            'doctor_uuids'  => [$doctors[0]->getUuid()],
            'national_code' => '1212121212',
            'address'       => 'یزد، خیابان تست',
            'permissions'   => ['version' => 1, 'resources' => ['patients' => ['view' => true]]],
        ]);
        $secretaryUuid = $assigned['data']['secretary_uuid'];

        // adding a second doctor must clone the person's profile onto the new link
        $this->authJson('PUT', '/api/v1/secretaries/clinic/' . $clinic->getUuid() . '/doctors', $owner, [
            'secretary_uuid' => $secretaryUuid,
            'doctor_uuids'   => [$doctors[0]->getUuid(), $doctors[1]->getUuid()],
        ]);
        $this->assertSame(200, $this->responseCode());

        $rows = $this->authJson('GET', '/api/v1/secretaries/clinic/' . $clinic->getUuid(), $owner);
        $this->assertCount(2, $rows['data']);
        foreach ($rows['data'] as $row) {
            $this->assertSame('1212121212', $row['national_code']);
            $this->assertSame('یزد، خیابان تست', $row['address']);
            $this->assertTrue($row['permissions']['patients']['view']);
        }
    }

    public function testForeignOwnerCannotSync(): void
    {
        [, $clinic, ] = $this->makeClinicWithDoctors(1);
        $intruder = $this->createUser(['ROLE_CLINIC']);

        $this->authJson('PUT', '/api/v1/secretaries/clinic/' . $clinic->getUuid() . '/doctors', $intruder, [
            'secretary_uuid' => 'whatever',
            'doctor_uuids'   => [],
        ]);

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