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

/**
 * Provider DBAL (lecture seule) du rapport absences-par-type.
 *
 * N'importe AUCUNE entite : couplage au seul schema SQL physique. La valeur du
 * statut filtre ('approved') correspond au backing-value de
 * App\Module\Absence\Domain\Enum\AbsenceStatus::Approved, mais est ecrite ici
 * en litteral pour eviter tout import inter-module. Tous les filtres sont
 * passes en bound params. Les bornes de date sont facultatives et sans defaut.
 *
 * @implements ProviderInterface<AbsencesByTypeOutput>
 */
final readonly class AbsencesByTypeProvider implements ProviderInterface
{
    use ReportFilterTrait;

    /**
     * Backing-value de AbsenceStatus::Approved (litteral pour rester decouple).
     */
    private const string STATUS_APPROVED = 'approved';

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

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

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

        $qb = $this->connection->createQueryBuilder()
            ->select(
                'ar.type AS type',
                'COUNT(ar.id) AS cnt',
                'COALESCE(SUM(ar.counted_days), 0) AS total_days',
            )
            ->from('absence_request', 'ar')
            ->where('ar.status = :status')
            ->setParameter('status', self::STATUS_APPROVED)
            ->groupBy('ar.type')
            ->orderBy('total_days', 'DESC')
        ;

        if (null !== $from) {
            $qb->andWhere('ar.start_date >= :from')->setParameter('from', $from);
        }
        if (null !== $to) {
            $qb->andWhere('ar.end_date < :to')->setParameter('to', $to);
        }
        if (null !== $userId) {
            $qb->andWhere('ar.user_id = :userId')->setParameter('userId', $userId);
        }

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

        return array_map(static fn (array $row): AbsencesByTypeOutput => new AbsencesByTypeOutput(
            type: (string) $row['type'],
            count: (int) $row['cnt'],
            totalDays: round((float) $row['total_days'], 2),
        ), $rows);
    }
}
