<?php

declare(strict_types=1);

namespace App\Controller;

use App\Entity\ComposantPieceSlot;
use App\Entity\ComposantProductSlot;
use App\Entity\MachineComponentLink;
use App\Entity\MachinePieceLink;
use App\Entity\MachineProductLink;
use App\Entity\PieceProductSlot;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;

final class UsedInController extends AbstractController
{
    public function __construct(
        private readonly EntityManagerInterface $em,
    ) {}

    #[Route('/api/composants/{id}/used-in', name: 'api_composant_used_in', methods: ['GET'])]
    public function composantUsedIn(string $id): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        return new JsonResponse([
            'machines' => $this->findMachinesUsingComposant($id),
        ]);
    }

    #[Route('/api/pieces/{id}/used-in', name: 'api_piece_used_in', methods: ['GET'])]
    public function pieceUsedIn(string $id): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        return new JsonResponse([
            'machines'   => $this->findMachinesUsingPiece($id),
            'composants' => $this->findComposantsUsingPiece($id),
        ]);
    }

    #[Route('/api/products/{id}/used-in', name: 'api_product_used_in', methods: ['GET'])]
    public function productUsedIn(string $id): JsonResponse
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        return new JsonResponse([
            'machines'   => $this->findMachinesUsingProduct($id),
            'composants' => $this->findComposantsUsingProduct($id),
            'pieces'     => $this->findPiecesUsingProduct($id),
        ]);
    }

    /**
     * @return list<array{id: string, name: null|string, site: null|array{id: string, name: null|string}}>
     */
    private function findMachinesUsingComposant(string $id): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select('m.id', 'm.name', 's.id as siteId', 's.name as siteName')
            ->from(MachineComponentLink::class, 'mcl')
            ->join('mcl.machine', 'm')
            ->leftJoin('m.site', 's')
            ->where('mcl.composant = :id')
            ->setParameter('id', $id)
            ->groupBy('m.id', 'm.name', 's.id', 's.name')
            ->orderBy('m.name', 'ASC')
        ;

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

    /**
     * @return list<array{id: string, name: null|string, site: null|array{id: string, name: null|string}}>
     */
    private function findMachinesUsingPiece(string $id): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select('m.id', 'm.name', 's.id as siteId', 's.name as siteName')
            ->from(MachinePieceLink::class, 'mpl')
            ->join('mpl.machine', 'm')
            ->leftJoin('m.site', 's')
            ->where('mpl.piece = :id')
            ->setParameter('id', $id)
            ->groupBy('m.id', 'm.name', 's.id', 's.name')
            ->orderBy('m.name', 'ASC')
        ;

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

    /**
     * @return list<array{id: string, name: null|string, site: null|array{id: string, name: null|string}}>
     */
    private function findMachinesUsingProduct(string $id): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select('m.id', 'm.name', 's.id as siteId', 's.name as siteName')
            ->from(MachineProductLink::class, 'mpl')
            ->join('mpl.machine', 'm')
            ->leftJoin('m.site', 's')
            ->where('mpl.product = :id')
            ->setParameter('id', $id)
            ->groupBy('m.id', 'm.name', 's.id', 's.name')
            ->orderBy('m.name', 'ASC')
        ;

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

    /**
     * @return list<array{id: string, name: null|string}>
     */
    private function findComposantsUsingPiece(string $id): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select('c.id', 'c.name')
            ->from(ComposantPieceSlot::class, 'cps')
            ->join('cps.composant', 'c')
            ->where('cps.selectedPiece = :id')
            ->setParameter('id', $id)
            ->groupBy('c.id', 'c.name')
            ->orderBy('c.name', 'ASC')
        ;

        return array_values(array_map(
            static fn (array $row): array => [
                'id'   => $row['id'],
                'name' => $row['name'],
            ],
            $qb->getQuery()->getArrayResult(),
        ));
    }

    /**
     * @return list<array{id: string, name: null|string}>
     */
    private function findComposantsUsingProduct(string $id): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select('c.id', 'c.name')
            ->from(ComposantProductSlot::class, 'cps')
            ->join('cps.composant', 'c')
            ->where('cps.selectedProduct = :id')
            ->setParameter('id', $id)
            ->groupBy('c.id', 'c.name')
            ->orderBy('c.name', 'ASC')
        ;

        return array_values(array_map(
            static fn (array $row): array => [
                'id'   => $row['id'],
                'name' => $row['name'],
            ],
            $qb->getQuery()->getArrayResult(),
        ));
    }

    /**
     * @return list<array{id: string, name: null|string}>
     */
    private function findPiecesUsingProduct(string $id): array
    {
        $qb = $this->em->createQueryBuilder();
        $qb->select('p.id', 'p.name')
            ->from(PieceProductSlot::class, 'pps')
            ->join('pps.piece', 'p')
            ->where('pps.selectedProduct = :id')
            ->setParameter('id', $id)
            ->groupBy('p.id', 'p.name')
            ->orderBy('p.name', 'ASC')
        ;

        return array_values(array_map(
            static fn (array $row): array => [
                'id'   => $row['id'],
                'name' => $row['name'],
            ],
            $qb->getQuery()->getArrayResult(),
        ));
    }

    /**
     * @param array<int, array<string, mixed>> $rows
     *
     * @return list<array{id: string, name: null|string, site: null|array{id: string, name: null|string}}>
     */
    private function formatMachineResults(array $rows): array
    {
        return array_values(array_map(
            static fn (array $row): array => [
                'id'   => $row['id'],
                'name' => $row['name'],
                'site' => $row['siteId']
                    ? ['id' => $row['siteId'], 'name' => $row['siteName']]
                    : null,
            ],
            $rows,
        ));
    }
}
