<?php

declare(strict_types=1);

namespace App\Module\Reporting\Infrastructure\ApiPlatform\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Module\Reporting\Application\DTO\TimePerUserOutput;
use Doctrine\DBAL\Connection;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
 * Provider DBAL (lecture seule) du rapport heures-par-utilisateur.
 *
 * N'importe AUCUNE entite : couplage au seul schema SQL physique. La table
 * "user" est un mot reserve PostgreSQL et reste systematiquement quotee. Tous
 * les filtres sont passes en bound params.
 *
 * @implements ProviderInterface<TimePerUserOutput>
 */
final readonly class TimePerUserProvider implements ProviderInterface
{
    use ReportFilterTrait;

    public function __construct(
        #[Autowire(service: 'doctrine.dbal.default_connection')]
        private Connection $connection,
    ) {}

    /**
     * @return list<TimePerUserOutput>
     */
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): array
    {
        /** @var array<string, mixed> $filters */
        $filters = $context['filters'] ?? [];

        [$from, $to] = $this->resolveDateRange($filters);
        $projectId   = $this->validatePositiveInt($filters['projectId'] ?? null, 'projectId');

        $qb = $this->connection->createQueryBuilder()
            ->select(
                'u.id AS user_id',
                'u.username AS username',
                "TRIM(COALESCE(u.first_name, '') || ' ' || COALESCE(u.last_name, '')) AS full_name",
                'COALESCE(SUM(EXTRACT(EPOCH FROM (te.stopped_at - te.started_at)) / 3600), 0) AS hours',
                'COUNT(te.id) AS entry_count',
            )
            ->from('time_entry', 'te')
            ->innerJoin('te', '"user"', 'u', 'te.user_id = u.id')
            ->where('te.stopped_at IS NOT NULL')
            ->andWhere('te.started_at >= :from')
            ->andWhere('te.started_at < :to')
            ->setParameter('from', $from)
            ->setParameter('to', $to)
            ->groupBy('u.id')
            ->addGroupBy('u.username')
            ->addGroupBy('u.first_name')
            ->addGroupBy('u.last_name')
            ->orderBy('hours', 'DESC')
        ;

        if (null !== $projectId) {
            $qb->andWhere('te.project_id = :projectId')->setParameter('projectId', $projectId);
        }

        /** @var list<array<string, mixed>> $rows */
        $rows = $qb->executeQuery()->fetchAllAssociative();

        return array_map(static fn (array $row): TimePerUserOutput => new TimePerUserOutput(
            userId: (int) $row['user_id'],
            username: (string) $row['username'],
            fullName: (string) $row['full_name'],
            hours: round((float) $row['hours'], 2),
            entryCount: (int) $row['entry_count'],
        ), $rows);
    }
}
