<?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\Infrastructure\Security\SecurityUser;
use App\Scolarite\Domain\Model\Evaluation\EvaluationStatus;
use App\Scolarite\Infrastructure\Api\Resource\ClassStatisticsResource;
use App\Shared\Infrastructure\Tenant\TenantContext;
use Doctrine\DBAL\Connection;

use function in_array;

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

/**
 * @implements ProviderInterface<ClassStatisticsResource>
 */
final readonly class ClassStatisticsProvider implements ProviderInterface
{
    public function __construct(
        private Connection $connection,
        private TenantContext $tenantContext,
        private Security $security,
    ) {
    }

    #[Override]
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): ClassStatisticsResource
    {
        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.');
        }

        $isStaff = $this->hasAnyRole($user->getRoles(), [
            Role::ADMIN->value,
            Role::PROF->value,
            Role::VIE_SCOLAIRE->value,
        ]);

        if (!$isStaff) {
            throw new AccessDeniedHttpException('Accès réservé au personnel éducatif.');
        }

        /** @var string $classId */
        $classId = $uriVariables['classId'];
        $tenantId = (string) $this->tenantContext->getCurrentTenantId();

        $resource = new ClassStatisticsResource();
        $resource->classId = $classId;

        $rows = $this->connection->fetchAllAssociative(
            'SELECT e.id as evaluation_id, e.title, es.average, es.min_grade, es.max_grade, es.median_grade, es.graded_count
             FROM evaluations e
             LEFT JOIN evaluation_statistics es ON es.evaluation_id = e.id
             WHERE e.class_id = :class_id
             AND e.tenant_id = :tenant_id
             AND e.status != :deleted
             AND e.grades_published_at IS NOT NULL
             ORDER BY e.evaluation_date DESC',
            [
                'class_id' => $classId,
                'tenant_id' => $tenantId,
                'deleted' => EvaluationStatus::DELETED->value,
            ],
        );

        foreach ($rows as $row) {
            /** @var string $evaluationId */
            $evaluationId = $row['evaluation_id'];
            /** @var string $title */
            $title = $row['title'];
            /** @var string|float|null $average */
            $average = $row['average'];
            /** @var string|float|null $minGrade */
            $minGrade = $row['min_grade'];
            /** @var string|float|null $maxGrade */
            $maxGrade = $row['max_grade'];
            /** @var string|float|null $medianGrade */
            $medianGrade = $row['median_grade'];
            /** @var string|int|null $gradedCount */
            $gradedCount = $row['graded_count'];

            $resource->evaluations[] = [
                'evaluationId' => $evaluationId,
                'title' => $title,
                'average' => $average !== null ? (float) $average : null,
                'min' => $minGrade !== null ? (float) $minGrade : null,
                'max' => $maxGrade !== null ? (float) $maxGrade : null,
                'median' => $medianGrade !== null ? (float) $medianGrade : null,
                'gradedCount' => $gradedCount !== null ? (int) $gradedCount : 0,
            ];
        }

        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;
    }
}
