<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Api\Provider;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Infrastructure\Security\SecurityUser;
use App\Scolarite\Domain\Repository\StudentAverageRepository;
use App\Scolarite\Infrastructure\Api\Resource\StudentAveragesResource;
use App\Shared\Infrastructure\Tenant\TenantContext;

use function in_array;

use InvalidArgumentException;
use Override;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;

/**
 * @implements ProviderInterface<StudentAveragesResource>
 */
final readonly class StudentAveragesProvider implements ProviderInterface
{
    public function __construct(
        private StudentAverageRepository $studentAverageRepository,
        private TenantContext $tenantContext,
        private Security $security,
    ) {
    }

    #[Override]
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): StudentAveragesResource
    {
        if (!$this->tenantContext->hasTenant()) {
            throw new UnauthorizedHttpException('Bearer', 'Tenant non défini.');
        }

        $user = $this->security->getUser();

        if (!$user instanceof SecurityUser) {
            throw new UnauthorizedHttpException('Bearer', 'Authentification requise.');
        }

        /** @var string $studentId */
        $studentId = $uriVariables['studentId'];

        try {
            $userId = UserId::fromString($studentId);
        } catch (InvalidArgumentException) {
            throw new BadRequestHttpException('Identifiant d\'élève invalide.');
        }

        // L'élève peut voir ses propres moyennes, les enseignants et admins aussi
        $isOwner = $user->userId() === $studentId;
        $isStaff = $this->hasAnyRole($user->getRoles(), [
            Role::ADMIN->value,
            Role::PROF->value,
            Role::VIE_SCOLAIRE->value,
        ]);

        if (!$isOwner && !$isStaff) {
            throw new AccessDeniedHttpException('Accès non autorisé aux moyennes de cet élève.');
        }

        $tenantId = $this->tenantContext->getCurrentTenantId();

        /** @var array<string, mixed> $filters */
        $filters = $context['filters'] ?? [];
        /** @var string|null $periodId */
        $periodId = $filters['periodId'] ?? null;

        $resource = new StudentAveragesResource();
        $resource->studentId = $studentId;
        $resource->periodId = $periodId;

        if ($periodId === null) {
            return $resource;
        }

        $resource->subjectAverages = $this->studentAverageRepository->findDetailedSubjectAveragesForStudent(
            $userId,
            $periodId,
            $tenantId,
        );

        $resource->generalAverage = $this->studentAverageRepository->findGeneralAverageForStudent(
            $userId,
            $periodId,
            $tenantId,
        );

        return $resource;
    }

    /**
     * @param list<string> $userRoles
     * @param list<string> $allowedRoles
     */
    private function hasAnyRole(array $userRoles, array $allowedRoles): bool
    {
        foreach ($userRoles as $role) {
            if (in_array($role, $allowedRoles, true)) {
                return true;
            }
        }

        return false;
    }
}
