<?php
/**
 * Plugin to prevent deletion of tables belonging to disabled modules
 */

namespace Autogedal\TableProtection\Plugin;

use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\Config\ConfigOptionsListConstants;
use Magento\Framework\Setup\Declaration\Schema\Diff\DiffManager;
use Magento\Framework\Setup\Declaration\Schema\Diff\Diff;
use Magento\Framework\Setup\Declaration\Schema\Dto\Table;
use Psr\Log\LoggerInterface;

/**
 * Plugin to prevent deletion of tables belonging to disabled modules
 */
class DisabledModuleTableProtection
{
    /**
     * @var DeploymentConfig
     */
    private $deploymentConfig;

    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @var array|null
     */
    private $disabledModules = null;

    /**
     * Constructor
     *
     * @param DeploymentConfig $deploymentConfig
     * @param LoggerInterface $logger
     */
    public function __construct(DeploymentConfig $deploymentConfig, LoggerInterface $logger)
    {
        $this->deploymentConfig = $deploymentConfig;
        $this->logger = $logger;
    }

    /**
     * Get list of disabled modules
     *
     * @return array
     */
    private function getDisabledModules(): array
    {
        if ($this->disabledModules === null) {
            $this->disabledModules = [];
            $moduleConfig = $this->deploymentConfig->get(ConfigOptionsListConstants::KEY_MODULES);
            
            if ($moduleConfig) {
                foreach ($moduleConfig as $moduleName => $isEnabled) {
                    if (!$isEnabled) {
                        $this->disabledModules[] = $moduleName;
                    }
                }
            }
        }
        
        return $this->disabledModules;
    }

    /**
     * Convert CamelCase string to snake_case
     *
     * @param string $input
     * @return string
     */
    private function camelCaseToSnakeCase(string $input): string
    {
        // Convert CamelCase to snake_case (e.g., CategoryReviews -> category_reviews)
        return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $input));
    }

    /**
     * Generate table name patterns for a module
     *
     * @param string $moduleName
     * @return array
     */
    private function generateTablePatterns(string $moduleName): array
    {
        $parts = explode('_', $moduleName);
        $patterns = [];
        
        if (count($parts) >= 2) {
            $vendor = strtolower($parts[0]);
            $module = $parts[1];
            
            // Pattern 1: full module name lowercase with underscore (e.g., autogedal_categoryreviews)
            $fullName = strtolower(implode('_', $parts));
            $patterns[] = $fullName;
            
            // Pattern 2: Convert module part from CamelCase to snake_case
            $moduleSnakeCase = $this->camelCaseToSnakeCase($module);
            
            // Pattern 3: module snake_case without vendor (e.g., category_reviews)
            $patterns[] = $moduleSnakeCase;
            
            // Pattern 4: vendor + module snake_case (e.g., autogedal_category_reviews)
            $patterns[] = $vendor . '_' . $moduleSnakeCase;
            
            // Pattern 5: vendor + module lowercase (e.g., autogedal_categoryreviews)
            $patterns[] = $vendor . '_' . strtolower($module);
            
            // Pattern 6: just module lowercase (e.g., categoryreviews)
            $patterns[] = strtolower($module);
        }
        
        // Remove duplicates and return
        return array_unique($patterns);
    }

    /**
     * Check if table belongs to a disabled module based on table name patterns
     *
     * @param string $tableName
     * @return bool
     */
    private function belongsToDisabledModule(string $tableName): bool
    {
        $disabledModules = $this->getDisabledModules();
        
        foreach ($disabledModules as $moduleName) {
            $patterns = $this->generateTablePatterns($moduleName);
            
            foreach ($patterns as $pattern) {
                if (strpos($tableName, $pattern) === 0) {
                    return true;
                }
            }
        }
        
        return false;
    }

    /**
     * Filter out tables that belong to disabled modules before registering them for removal
     *
     * @param DiffManager $subject
     * @param Diff $diff
     * @param array $generatedElements
     * @return array
     */
    public function beforeRegisterRemoval(DiffManager $subject, Diff $diff, array $generatedElements): array
    {
        $this->logger->debug("TableProtection: beforeRegisterRemoval called with " . count($generatedElements) . " elements");
        
        $filteredElements = [];
        $protectedTables = [];
        
        foreach ($generatedElements as $key => $element) {
            if ($element instanceof Table) {
                $tableName = $element->getName();
                $this->logger->debug("TableProtection: Checking table: " . $tableName);
                
                // Skip removal if table belongs to a disabled module
                if ($this->belongsToDisabledModule($tableName)) {
                    $protectedTables[] = $tableName;
                    $this->logger->debug("TableProtection: PROTECTING table: " . $tableName);
                    continue;
                }
            }
            
            $filteredElements[$key] = $element;
        }
        
        // Log protected tables for debugging
        if (!empty($protectedTables)) {
            $this->logger->debug("TableProtection: Protected tables from deletion: " . implode(', ', $protectedTables));
        } else {
            $this->logger->debug("TableProtection: No tables protected this time");
        }
        
        return [$diff, $filteredElements];
    }
}