<?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\TimePerProjectOutput;
use Doctrine\DBAL\Connection;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
 * Provider DBAL (lecture seule) du rapport heures-par-projet.
 *
 * N'importe AUCUNE entite : couplage au seul schema SQL physique. Tous les
 * filtres sont passes en bound params (jamais interpoles).
 *
 * @implements ProviderInterface<TimePerProjectOutput>
 */
final readonly class TimePerProjectProvider implements ProviderInterface
{
    use ReportFilterTrait;

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

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

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

        $qb = $this->connection->createQueryBuilder()
            ->select(
                'p.id AS project_id',
                'p.code AS project_code',
                'p.name AS project_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', 'project', 'p', 'te.project_id = p.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('p.id')
            ->addGroupBy('p.code')
            ->addGroupBy('p.name')
            ->orderBy('hours', 'DESC')
        ;

        if (null !== $userId) {
            $qb->andWhere('te.user_id = :userId')->setParameter('userId', $userId);
        }
        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): TimePerProjectOutput => new TimePerProjectOutput(
            projectId: (int) $row['project_id'],
            projectCode: (string) $row['project_code'],
            projectName: (string) $row['project_name'],
            hours: round((float) $row['hours'], 2),
            entryCount: (int) $row['entry_count'],
        ), $rows);
    }
}
