<?php

namespace App\Tests\Staff;

use App\Auth\Repository\UserRepository;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Doctor\Entity\Doctor;
use App\Staff\Entity\ClinicStaff;
use App\Staff\Repository\ClinicStaffRepository;
use App\Staff\Service\StaffAccountService;
use App\Tests\ApiTestCase;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

/**
 * داشبورد پرسنل + گاردِ default-deny: نقش staff فقط داشبورد خودش را می‌بیند و
 * بقیهٔ API برایش بسته است.
 */
class StaffDashboardAccessTest extends ApiTestCase
{
    private function accounts(): StaffAccountService
    {
        return new StaffAccountService(
            static::getContainer()->get(UserRepository::class),
            static::getContainer()->get(ClinicStaffRepository::class),
            static::getContainer()->get(UserPasswordHasherInterface::class),
        );
    }

    /** @return array{0: Doctor, 1: ClinicStaff, 2: string} پزشکِ مالک، ردیف پرسنل، توکن پرسنل */
    private function staffFixture(bool $active = true): array
    {
        $ownerUser = $this->createUser(['ROLE_DOCTOR']);
        $doctor    = new Doctor($ownerUser, 'دکتر تست');
        $this->em->persist($doctor);
        $this->em->flush();

        $staff = new ClinicStaff('doctor', $doctor->getId(), 'زهرا احمدی');
        $staff->setJobTitle('پرستار');
        $staff->setActive($active);
        $this->em->persist($staff);
        $this->em->flush();

        $mobile     = '0912' . str_pad((string) random_int(0, 9_999_999), 7, '0', STR_PAD_LEFT);
        $staffUser  = $this->accounts()->attachAccount($staff, $mobile, 'Staff@1234', $ownerUser);
        $token      = static::getContainer()->get(JWTTokenManagerInterface::class)->create($staffUser);

        // محیط فعال؛ در حالت واقعی /oauth/userinfo آن را برای تک‌محیطی‌ها ست می‌کند.
        $this->client->request('GET', '/oauth/userinfo', [], [], ['HTTP_AUTHORIZATION' => 'Bearer ' . $token]);

        return [$doctor, $staff, $token];
    }

    private function get(string $path, string $token): int
    {
        $this->client->request('GET', $path, [], [], ['HTTP_AUTHORIZATION' => 'Bearer ' . $token]);

        return $this->client->getResponse()->getStatusCode();
    }

    private function json(): array
    {
        return json_decode($this->client->getResponse()->getContent(), true);
    }

    public function testDashboardListsAssignedServices(): void
    {
        [$doctor, $staff, $token] = $this->staffFixture();

        $section = new ServiceSection('doctor', $doctor->getId(), 'تزریقات');
        $item    = new ServiceItem($section, 'سرم', 1_000_000);
        $item->setStaffMembers([$staff]);
        $other   = new ServiceItem($section, 'سرویس بدون پرسنل', 500_000);
        $this->em->persist($section);
        $this->em->persist($item);
        $this->em->persist($other);
        $this->em->flush();

        self::assertSame(200, $this->get('/api/v1/dashboard/staff', $token));
        $data = $this->json()['data'];

        self::assertSame('doctor', $data['scope']);
        self::assertSame($staff->getUuid(), $data['staff']['uuid']);
        self::assertSame(1, $data['stats']['services']);
        self::assertCount(1, $data['services']);
        self::assertSame('سرم', $data['services'][0]['name']);
        self::assertSame('تزریقات', $data['services'][0]['section_name']);
    }

    /** مرزی: پرسنلِ بدون سرویس → لیست خالی، نه خطا. */
    public function testDashboardWithoutServicesReturnsEmptyList(): void
    {
        [, , $token] = $this->staffFixture();

        self::assertSame(200, $this->get('/api/v1/dashboard/staff', $token));
        $data = $this->json()['data'];

        self::assertSame([], $data['services']);
        self::assertSame(0, $data['stats']['services']);
        self::assertSame([], $data['today_appointments']);
    }

    public function testInactiveStaffIsDenied(): void
    {
        [, , $token] = $this->staffFixture(active: false);

        self::assertSame(403, $this->get('/api/v1/dashboard/staff', $token));
        self::assertSame('ERR_FORBIDDEN_001', $this->json()['errors'][0]['code']);
    }

    /** گارد: هر مسیر API خارج از allowlist برای نقش staff بسته است. */
    public function testOtherApiRoutesAreForbidden(): void
    {
        [, , $token] = $this->staffFixture();

        foreach (['/api/v1/service-items', '/api/v1/staff', '/api/v1/patients', '/api/v1/dashboard/clinic'] as $path) {
            self::assertSame(403, $this->get($path, $token), $path . ' must be forbidden for staff');
            self::assertSame('ERR_FORBIDDEN_001', $this->json()['errors'][0]['code'], $path);
        }

        // مسیرهای مجاز دست‌نخورده‌اند
        self::assertSame(200, $this->get('/oauth/userinfo', $token));
    }

    /** گارد نباید نقش‌های دیگر را بگیرد، حتی اگر کاربر هم‌زمان پرسنل باشد. */
    public function testGuardSkipsUsersWithStrongerRole(): void
    {
        [, , $token] = $this->staffFixture();

        $data      = json_decode($this->client->getResponse()->getContent(), true);
        $staffUser = static::getContainer()->get(UserRepository::class)->findByUuid($data['data']['uuid'] ?? '');
        self::assertNotNull($staffUser);

        $staffUser->addRole('ROLE_DOCTOR');
        $this->em->flush();
        $token = static::getContainer()->get(JWTTokenManagerInterface::class)->create($staffUser);

        self::assertNotSame(403, $this->get('/api/v1/service-items', $token));
    }
}
