<?php

declare(strict_types=1);

namespace App\EventSubscriber;

use App\Entity\AuditLog;
use App\Entity\CustomFieldValue;
use App\Entity\Machine;
use App\Entity\MachineComponentLink;
use App\Entity\MachinePieceLink;
use App\Entity\MachineProductLink;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\UnitOfWork;

#[AsDoctrineListener(event: Events::onFlush)]
final class MachineAuditSubscriber extends AbstractAuditSubscriber
{
    public function onFlush(OnFlushEventArgs $args): void
    {
        // Let parent handle regular Machine entity changes (fields, collections, custom fields)
        parent::onFlush($args);

        // Now handle link entity changes
        $em = $args->getObjectManager();
        if (!$em instanceof EntityManagerInterface) {
            return;
        }

        $uow            = $em->getUnitOfWork();
        $actorProfileId = $this->actorProfileResolver->resolve();

        $this->processLinkChanges($em, $uow, $actorProfileId);
    }

    protected function supports(object $entity): bool
    {
        return $entity instanceof Machine;
    }

    protected function entityType(): string
    {
        return 'machine';
    }

    protected function hasCollectionTracking(): bool
    {
        return true;
    }

    protected function getOwnerFromCustomFieldValue(CustomFieldValue $cfv): ?object
    {
        $owner = $cfv->getMachine();

        return $owner instanceof Machine ? $owner : null;
    }

    protected function snapshotEntity(object $entity): array
    {
        $customFieldValues = [];
        foreach ($entity->getCustomFieldValues() as $cfv) {
            $customFieldValues[] = [
                'id'        => $cfv->getId(),
                'fieldName' => $cfv->getCustomField()?->getName(),
                'fieldId'   => $cfv->getCustomField()?->getId(),
                'value'     => $cfv->getValue(),
            ];
        }

        $componentLinks = [];
        foreach ($entity->getComponentLinks() as $link) {
            $componentLinks[] = [
                'id'            => $link->getId(),
                'composantId'   => $link->getComposant()?->getId(),
                'composantName' => $link->getComposant()?->getName(),
                'modelTypeId'   => $link->getModelType()?->getId(),
            ];
        }

        $pieceLinks = [];
        foreach ($entity->getPieceLinks() as $link) {
            $pieceLinks[] = [
                'id'          => $link->getId(),
                'pieceId'     => $link->getPiece()?->getId(),
                'pieceName'   => $link->getPiece()?->getName(),
                'quantity'    => $link->getQuantity(),
                'modelTypeId' => $link->getModelType()?->getId(),
            ];
        }

        $productLinks = [];
        foreach ($entity->getProductLinks() as $link) {
            $productLinks[] = [
                'id'          => $link->getId(),
                'productId'   => $link->getProduct()?->getId(),
                'productName' => $link->getProduct()?->getName(),
                'modelTypeId' => $link->getModelType()?->getId(),
            ];
        }

        return [
            'id'              => $entity->getId(),
            'name'            => $this->safeGet($entity, 'getName'),
            'reference'       => $this->safeGet($entity, 'getReference'),
            'prix'            => $this->safeGet($entity, 'getPrix'),
            'site'            => $this->normalizeValue($this->safeGet($entity, 'getSite')),
            'constructeurIds' => array_map(
                fn ($link) => [
                    'id'                => $link->getConstructeur()->getId(),
                    'name'              => $link->getConstructeur()->getName(),
                    'supplierReference' => $link->getSupplierReference(),
                ],
                $entity->getConstructeurLinks()->toArray(),
            ),
            'customFieldValues' => $customFieldValues,
            'componentLinks'    => $componentLinks,
            'pieceLinks'        => $pieceLinks,
            'productLinks'      => $productLinks,
            'version'           => $this->safeGet($entity, 'getVersion'),
        ];
    }

    private function processLinkChanges(EntityManagerInterface $em, UnitOfWork $uow, ?string $actorProfileId): void
    {
        $machineChanges = [];

        // Detect inserted links
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
            $info = $this->extractLinkInfo($entity, 'added');
            if (null === $info) {
                continue;
            }
            $machineId = (string) $info['machine']->getId();
            if ('' === $machineId) {
                continue;
            }
            $machineChanges[$machineId] ??= ['machine' => $info['machine'], 'diffs' => []];
            $machineChanges[$machineId]['diffs'][$info['diffKey']] = [
                'from' => null,
                'to'   => $info['diffValue'],
            ];
        }

        // Detect deleted links
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
            $info = $this->extractLinkInfo($entity, 'removed');
            if (null === $info) {
                continue;
            }
            $machineId = (string) $info['machine']->getId();
            if ('' === $machineId) {
                continue;
            }
            $machineChanges[$machineId] ??= ['machine' => $info['machine'], 'diffs' => []];
            $machineChanges[$machineId]['diffs'][$info['diffKey']] = [
                'from' => $info['diffValue'],
                'to'   => null,
            ];
        }

        // Create audit logs for each affected machine
        foreach ($machineChanges as $machineId => $change) {
            $machine = $change['machine'];
            $diff    = $change['diffs'];

            if ([] === $diff) {
                continue;
            }

            $version  = $this->incrementEntityVersion($machine, $em, $uow);
            $snapshot = $this->snapshotEntity($machine);

            $this->persistAuditLog(
                $em,
                new AuditLog('machine', $machineId, 'update', $diff, $snapshot, $actorProfileId, $version),
            );
        }
    }

    /**
     * @return null|array{machine: Machine, diffKey: string, diffValue: array{id: string, name: string}}
     */
    private function extractLinkInfo(object $entity, string $action): ?array
    {
        if ($entity instanceof MachineComponentLink) {
            return [
                'machine'   => $entity->getMachine(),
                'diffKey'   => $action.'Component',
                'diffValue' => [
                    'id'   => $entity->getComposant()?->getId() ?? $entity->getModelType()?->getId(),
                    'name' => $entity->getComposant()?->getName() ?? $entity->getModelType()?->getName() ?? 'Catégorie seule',
                ],
            ];
        }

        if ($entity instanceof MachinePieceLink) {
            return [
                'machine'   => $entity->getMachine(),
                'diffKey'   => $action.'Piece',
                'diffValue' => [
                    'id'   => $entity->getPiece()?->getId() ?? $entity->getModelType()?->getId(),
                    'name' => $entity->getPiece()?->getName() ?? $entity->getModelType()?->getName() ?? 'Catégorie seule',
                ],
            ];
        }

        if ($entity instanceof MachineProductLink) {
            return [
                'machine'   => $entity->getMachine(),
                'diffKey'   => $action.'Product',
                'diffValue' => [
                    'id'   => $entity->getProduct()?->getId() ?? $entity->getModelType()?->getId(),
                    'name' => $entity->getProduct()?->getName() ?? $entity->getModelType()?->getName() ?? 'Catégorie seule',
                ],
            ];
        }

        return null;
    }
}
