<?php

declare(strict_types=1);

namespace App\Controller;

use App\Entity\Composant;
use App\Entity\MachineComponentLink;
use App\Entity\MachinePieceLink;
use App\Entity\MachineProductLink;
use App\Entity\Piece;
use App\Entity\Product;
use App\Enum\ModelCategory;
use App\Repository\ModelTypeRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/api/model_types/{id}')]
final class ModelTypeRelatedItemsController extends AbstractController
{
    public function __construct(
        private readonly ModelTypeRepository $modelTypes,
        private readonly EntityManagerInterface $em,
    ) {}

    #[Route('/related-items', name: 'api_model_type_related_items', methods: ['GET'])]
    public function relatedItems(string $id): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        $modelType = $this->modelTypes->find($id);

        if (!$modelType) {
            return new JsonResponse(
                ['message' => 'Catégorie introuvable.'],
                Response::HTTP_NOT_FOUND,
            );
        }

        $items = match ($modelType->getCategory()) {
            ModelCategory::COMPONENT => $this->fetchComposantsWithMachineCount($id),
            ModelCategory::PIECE     => $this->fetchPiecesWithMachineCount($id),
            ModelCategory::PRODUCT   => $this->fetchProductsWithMachineCount($id),
        };

        return new JsonResponse($items);
    }

    /**
     * @return list<array{id: string, name: string, reference: null|string, machineCount: int}>
     */
    private function fetchComposantsWithMachineCount(string $modelTypeId): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select(
            'c.id',
            'c.name',
            'c.reference',
            'COALESCE(c.referenceAuto, c.reference) as displayReference',
            'COUNT(DISTINCT mcl.id) as machineCount',
        )
            ->from(Composant::class, 'c')
            ->leftJoin(MachineComponentLink::class, 'mcl', 'WITH', 'mcl.composant = c')
            ->where('c.typeComposant = :modelTypeId')
            ->setParameter('modelTypeId', $modelTypeId)
            ->groupBy('c.id', 'c.name', 'c.reference', 'c.referenceAuto')
            ->orderBy('c.name', 'ASC')
        ;

        return $this->formatResults($qb->getQuery()->getArrayResult());
    }

    /**
     * @return list<array{id: string, name: string, reference: null|string, machineCount: int}>
     */
    private function fetchPiecesWithMachineCount(string $modelTypeId): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select(
            'p.id',
            'p.name',
            'p.reference',
            'COALESCE(p.referenceAuto, p.reference) as displayReference',
            'COUNT(DISTINCT mpl.id) as machineCount',
        )
            ->from(Piece::class, 'p')
            ->leftJoin(MachinePieceLink::class, 'mpl', 'WITH', 'mpl.piece = p')
            ->where('p.typePiece = :modelTypeId')
            ->setParameter('modelTypeId', $modelTypeId)
            ->groupBy('p.id', 'p.name', 'p.reference', 'p.referenceAuto')
            ->orderBy('p.name', 'ASC')
        ;

        return $this->formatResults($qb->getQuery()->getArrayResult());
    }

    /**
     * @return list<array{id: string, name: string, reference: null|string, machineCount: int}>
     */
    private function fetchProductsWithMachineCount(string $modelTypeId): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select(
            'pr.id',
            'pr.name',
            'pr.reference',
            'COUNT(DISTINCT mpl.id) as machineCount',
        )
            ->from(Product::class, 'pr')
            ->leftJoin(MachineProductLink::class, 'mpl', 'WITH', 'mpl.product = pr')
            ->where('pr.typeProduct = :modelTypeId')
            ->setParameter('modelTypeId', $modelTypeId)
            ->groupBy('pr.id', 'pr.name', 'pr.reference')
            ->orderBy('pr.name', 'ASC')
        ;

        return $this->formatResults($qb->getQuery()->getArrayResult());
    }

    /**
     * @param array<int, array<string, mixed>> $rows
     *
     * @return list<array{id: string, name: string, reference: null|string, machineCount: int}>
     */
    private function formatResults(array $rows): array
    {
        return array_values(array_map(
            static fn (array $row): array => [
                'id'        => (string) $row['id'],
                'name'      => (string) ($row['name'] ?? ''),
                'reference' => isset($row['displayReference']) && '' !== $row['displayReference']
                    ? (string) $row['displayReference']
                    : (isset($row['reference']) && '' !== $row['reference'] ? (string) $row['reference'] : null),
                'machineCount' => (int) $row['machineCount'],
            ],
            $rows,
        ));
    }
}
