<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Api\Provider;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Administration\Infrastructure\Security\SecurityUser;
use App\Scolarite\Domain\Model\Competency\CompetencyLevel;
use App\Scolarite\Domain\Repository\CustomCompetencyLevelRepository;
use App\Scolarite\Infrastructure\Api\Resource\StudentCompetencyProgressResource;
use App\Shared\Domain\Tenant\TenantId;
use App\Shared\Infrastructure\Tenant\TenantContext;

use function array_values;

use Doctrine\DBAL\Connection;

use function is_string;

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

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

    /** @return array<StudentCompetencyProgressResource> */
    #[Override]
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
    {
        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 $studentIdStr */
        $studentIdStr = $uriVariables['studentId'] ?? '';

        if (!is_string($studentIdStr) || $studentIdStr === '') {
            throw new NotFoundHttpException('Élève non trouvé.');
        }

        // Un élève ne peut consulter que sa propre progression
        if ($studentIdStr !== $user->userId() && !$this->security->isGranted('ROLE_PROF') && !$this->security->isGranted('ROLE_ADMIN')) {
            throw new AccessDeniedHttpException('Accès refusé.');
        }

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

        // Récupérer tous les résultats de compétences pour l'élève, avec historique
        $rows = $this->connection->fetchAllAssociative(
            'SELECT c.id AS competency_id, c.code AS competency_code, c.name AS competency_name,
                    scr.level_code, e.evaluation_date, e.title AS evaluation_title
             FROM student_competency_results scr
             JOIN competency_evaluations ce ON ce.id = scr.competency_evaluation_id
             JOIN competencies c ON c.id = ce.competency_id
             JOIN evaluations e ON e.id = ce.evaluation_id
             WHERE scr.student_id = :student_id
             AND scr.tenant_id = :tenant_id
             ORDER BY c.sort_order ASC, e.evaluation_date ASC',
            [
                'student_id' => $studentIdStr,
                'tenant_id' => $tenantId,
            ],
        );

        // Build level name map (custom levels override standard)
        $levelNameMap = [];
        $customLevels = $this->customLevelRepository->findByTenant(TenantId::fromString($tenantId));
        if ($customLevels !== []) {
            foreach ($customLevels as $cl) {
                $levelNameMap[$cl->code] = $cl->name;
            }
        } else {
            foreach (CompetencyLevel::cases() as $sl) {
                $levelNameMap[$sl->value] = $sl->label();
            }
        }

        // Grouper par compétence
        /** @var array<string, StudentCompetencyProgressResource> $progressByCompetency */
        $progressByCompetency = [];

        foreach ($rows as $row) {
            /** @var string $competencyId */
            $competencyId = $row['competency_id'];
            /** @var string $levelCode */
            $levelCode = $row['level_code'];
            /** @var string $evaluationDate */
            $evaluationDate = $row['evaluation_date'];
            /** @var string $evaluationTitle */
            $evaluationTitle = $row['evaluation_title'];

            if (!isset($progressByCompetency[$competencyId])) {
                $resource = new StudentCompetencyProgressResource();
                $resource->competencyId = $competencyId;
                /** @var string $code */
                $code = $row['competency_code'];
                $resource->competencyCode = $code;
                /** @var string $name */
                $name = $row['competency_name'];
                $resource->competencyName = $name;
                $progressByCompetency[$competencyId] = $resource;
            }

            $progressByCompetency[$competencyId]->history[] = [
                'date' => $evaluationDate,
                'levelCode' => $levelCode,
                'evaluationTitle' => $evaluationTitle,
            ];

            // Le dernier résultat (trié par date ASC) est le niveau actuel
            $progressByCompetency[$competencyId]->currentLevelCode = $levelCode;
            $progressByCompetency[$competencyId]->currentLevelName = $levelNameMap[$levelCode] ?? $levelCode;
        }

        return array_values($progressByCompetency);
    }
}
