<?php
/*
 * Copyright (c) 2023. RTM Business
 */

namespace WordpressModels\ORM;

use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ClassMetadataInfo;

/**
 * Doctrine EventSubscriber for injecting table prefixes for global database and plugin-specific entity-mappings.
 *
 * Entity- and plugin-specific prefixes can be registered using the `doctrine_plugin_entity_table_prefixes` hook.
 */
class TablePrefixEventSubscriber implements EventSubscriber
{

    /**
     * The wpdb::$prefix value
     * @var string
     */
    private readonly string $mainPrefix;
    /**
     * @var array<string, string>
     */
    private array $pluginPrefixes;

    public function __construct()
    {
        global $wpdb;
        $this->mainPrefix = $wpdb->prefix;
    }

    public function getSubscribedEvents()
    {
        return [Events::loadClassMetadata];
    }

    /**
     * Event handler for LoadClassMetadata event.
     *
     * Prefix any table with the self::$mainPrefix property. If the class in the event is a registered plugin entity,
     * prefix the table additionally with the given value from self::$pluginPrefix.
     *
     * @param LoadClassMetadataEventArgs $eventArgs
     * @return void
     */
    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        $classMetadata = $eventArgs->getClassMetadata();

        // initialize plugin class table prefixes
        if (!isset($this->pluginPrefixes)) {
            $this->pluginPrefixes = apply_filters('doctrine_plugin_entity_table_prefixes', []);
        }

        // get the plugin prefix for the current entity class
        $pluginPrefix = $this->pluginPrefixes[$classMetadata->getName()] ?? '';
        $prefix = $this->mainPrefix . ($pluginPrefix ? "{$pluginPrefix}_" : '');

        // Prefix the table name.
        if (!$classMetadata->isInheritanceTypeSingleTable() || $classMetadata->getName() === $classMetadata->rootEntityName) {
            $classMetadata->setPrimaryTable([
                'name' => $prefix . $classMetadata->getTableName()
            ]);
        }

        // Prefix the table name of each join table.
        foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) {
            if ($mapping['type'] == ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) {
                $mappedTableName = $mapping['joinTable']['name'];
                $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $prefix . $mappedTableName;
            }
        }
    }
}
