<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Infrastructure\Api\Provider;

use ApiPlatform\Metadata\GetCollection;
use App\Administration\Application\Query\GetParentsForStudent\GetParentsForStudentHandler;
use App\Administration\Domain\Model\StudentGuardian\RelationshipType;
use App\Administration\Domain\Model\StudentGuardian\StudentGuardian;
use App\Administration\Domain\Model\User\Email;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\User;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Domain\Repository\UserRepository;
use App\Administration\Infrastructure\Api\Provider\GuardiansForStudentProvider;
use App\Administration\Infrastructure\Api\Resource\StudentGuardianResource;
use App\Administration\Infrastructure\Persistence\InMemory\InMemoryStudentGuardianRepository;
use App\Administration\Infrastructure\Security\StudentGuardianVoter;
use App\Shared\Domain\Tenant\TenantId;
use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantContext;
use App\Shared\Infrastructure\Tenant\TenantId as InfraTenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

final class GuardiansForStudentProviderTest extends TestCase
{
    private const string TENANT_ID = '550e8400-e29b-41d4-a716-446655440002';
    private const string SUBDOMAIN = 'ecole-alpha';
    private const string STUDENT_ID = '550e8400-e29b-41d4-a716-446655440010';
    private const string GUARDIAN_ID = '550e8400-e29b-41d4-a716-446655440020';

    private InMemoryStudentGuardianRepository $repository;
    private TenantContext $tenantContext;

    protected function setUp(): void
    {
        $this->repository = new InMemoryStudentGuardianRepository();

        $this->tenantContext = new TenantContext();
        $this->tenantContext->setCurrentTenant(new TenantConfig(
            tenantId: InfraTenantId::fromString(self::TENANT_ID),
            subdomain: self::SUBDOMAIN,
            databaseUrl: 'postgresql://test',
        ));
    }

    #[Test]
    public function returnsGuardiansForStudent(): void
    {
        $link = StudentGuardian::lier(
            studentId: UserId::fromString(self::STUDENT_ID),
            guardianId: UserId::fromString(self::GUARDIAN_ID),
            relationshipType: RelationshipType::FATHER,
            tenantId: TenantId::fromString(self::TENANT_ID),
            createdAt: new DateTimeImmutable('2026-02-10 10:00:00'),
        );
        $this->repository->save($link);

        $provider = $this->createProvider();

        $results = $provider->provide(
            new GetCollection(),
            ['studentId' => self::STUDENT_ID],
        );

        self::assertCount(1, $results);
        self::assertInstanceOf(StudentGuardianResource::class, $results[0]);
        self::assertSame((string) $link->id, $results[0]->id);
        self::assertSame(self::GUARDIAN_ID, $results[0]->guardianId);
        self::assertSame('père', $results[0]->relationshipType);
        self::assertSame('Père', $results[0]->relationshipLabel);
    }

    #[Test]
    public function returnsEmptyArrayWhenNoGuardians(): void
    {
        $provider = $this->createProvider();

        $results = $provider->provide(
            new GetCollection(),
            ['studentId' => self::STUDENT_ID],
        );

        self::assertSame([], $results);
    }

    #[Test]
    public function throwsUnauthorizedWhenNoTenant(): void
    {
        $tenantContext = new TenantContext();

        $provider = $this->createProvider(tenantContext: $tenantContext);

        $this->expectException(UnauthorizedHttpException::class);

        $provider->provide(
            new GetCollection(),
            ['studentId' => self::STUDENT_ID],
        );
    }

    #[Test]
    public function throwsAccessDeniedWhenNotAuthorizedToViewStudent(): void
    {
        $authChecker = $this->createMock(AuthorizationCheckerInterface::class);
        $authChecker->method('isGranted')
            ->with(StudentGuardianVoter::VIEW_STUDENT, self::STUDENT_ID)
            ->willReturn(false);

        $provider = $this->createProvider(authorizationChecker: $authChecker);

        $this->expectException(AccessDeniedHttpException::class);

        $provider->provide(
            new GetCollection(),
            ['studentId' => self::STUDENT_ID],
        );
    }

    private function createProvider(
        ?TenantContext $tenantContext = null,
        ?AuthorizationCheckerInterface $authorizationChecker = null,
    ): GuardiansForStudentProvider {
        $guardianUser = User::creer(
            email: new Email('guardian@example.com'),
            role: Role::PARENT,
            tenantId: TenantId::fromString(self::TENANT_ID),
            schoolName: 'École Test',
            dateNaissance: null,
            createdAt: new DateTimeImmutable('2026-02-10 10:00:00'),
        );

        $userRepository = $this->createMock(UserRepository::class);
        $userRepository->method('get')->willReturn($guardianUser);

        $handler = new GetParentsForStudentHandler($this->repository, $userRepository);

        $tenantContext ??= $this->tenantContext;

        if ($authorizationChecker === null) {
            $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
            $authorizationChecker->method('isGranted')
                ->with(StudentGuardianVoter::VIEW_STUDENT, self::STUDENT_ID)
                ->willReturn(true);
        }

        return new GuardiansForStudentProvider(
            $handler,
            $tenantContext,
            $authorizationChecker,
        );
    }
}
