<?php

/*
 * This file is part of the Serenity package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Serenity\CoreBundle\Shared\Doctrine\EventListener;

use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadata;
use Serenity\CoreBundle\Shared\Feature\NestedTreeInterface;

#[AsDoctrineListener(event: Events::loadClassMetadata)]
class NestedTreeDynamicMappingListener
{
    public function loadClassMetadata(LoadClassMetadataEventArgs $args): void
    {
        $meta = $args->getClassMetadata();
        $refl = $meta->getReflectionClass();

        // On ignore mapped superclasses / embeddables si besoin
        if ($meta->isMappedSuperclass || $meta->isEmbeddedClass) {
            return;
        }

        // Applique uniquement aux entités qui implémentent ton interface
        if (!$refl->implementsInterface(NestedTreeInterface::class)) {
            return;
        }

        $this->mapNestedTreeFieldsAndAssociations($meta);
    }

    private function mapNestedTreeFieldsAndAssociations(ClassMetadata $meta): void
    {
        $class = $meta->name;

        // -------------------------
        // Champs (lvl / lft / rgt)
        // -------------------------
        if (!$meta->hasField('lvl')) {
            $meta->mapField([
                'fieldName' => 'lvl',
                'type' => 'integer',
                'nullable' => false,
                'options' => ['default' => 0],
            ]);
        }

        if (!$meta->hasField('lft')) {
            $meta->mapField([
                'fieldName' => 'lft',
                'type' => 'integer',
                'nullable' => false,
                'options' => ['default' => 0],
            ]);
        }

        if (!$meta->hasField('rgt')) {
            $meta->mapField([
                'fieldName' => 'rgt',
                'type' => 'integer',
                'nullable' => false,
                'options' => ['default' => 0],
            ]);
        }

        // -------------------------
        // Assoc root (ManyToOne)
        // -------------------------
        if (!$meta->hasAssociation('root')) {
            $meta->mapManyToOne([
                'fieldName' => 'root',
                'targetEntity' => $class,
                'joinColumns' => [[
                    'name' => 'root_id',
                    'referencedColumnName' => $meta->getSingleIdentifierColumnName(), // généralement "id"
                    'nullable' => true,
                    'onDelete' => 'CASCADE',
                ]],
            ]);
        }

        // -------------------------
        // Assoc parent (ManyToOne)
        // -------------------------
        if (!$meta->hasAssociation('parent')) {
            $meta->mapManyToOne([
                'fieldName' => 'parent',
                'targetEntity' => $class,
                'inversedBy' => 'children',
                'joinColumns' => [[
                    'name' => 'parent_id',
                    'referencedColumnName' => $meta->getSingleIdentifierColumnName(),
                    'nullable' => true,
                    'onDelete' => 'CASCADE',
                ]],
            ]);
        }

        // -------------------------
        // Assoc children (OneToMany)
        // -------------------------
        if (!$meta->hasAssociation('children')) {
            $meta->mapOneToMany([
                'fieldName' => 'children',
                'targetEntity' => $class,
                'mappedBy' => 'parent',
                // À toi de décider le comportement :
                'cascade' => ['persist'],      // menu: souvent pratique
                'orphanRemoval' => false,      // mets true si tu veux supprimer les enfants quand détachés
                'fetch' => ClassMetadata::FETCH_LAZY,
                'orderBy' => ['lft' => 'ASC'],
            ]);
        }

        // -------------------------
        // Indexes conseillés
        // -------------------------
        $meta->table['indexes'] ??= [];

        // Pour les requêtes "un arbre trié"
        $meta->table['indexes']['idx_root_lft'] ??= [
            'columns' => ['root_id', 'lft'],
        ];

        // Pour récupérer rapidement les enfants (adjacency)
        $meta->table['indexes']['idx_parent'] ??= [
            'columns' => ['parent_id'],
        ];
    }
}
