<?php namespace EvolutionCMS\Console\Packages;

use Illuminate\Console\Command;
use \EvolutionCMS;
use Illuminate\Support\Facades\File;

/**
 * PackageCommand - Discover and register ServiceProviders from custom packages
 *
 * This command scans composer.json files and generates provider config files
 * in core/custom/config/app/providers/ directory.
 *
 * Provider Priority System:
 * -------------------------
 * Providers can specify load priority in their composer.json:
 *
 * "extra": {
 *     "laravel": {
 *         "providers": ["Vendor\\Package\\SomeServiceProvider"],
 *         "priority": {
 *             "Vendor\\Package\\SomeServiceProvider": 1
 *         }
 *     }
 * }
 *
 * Priority 1-999: Creates file with prefix (e.g., 001_SomeServiceProvider.php)
 * Priority 0 (or not specified): Creates file without prefix
 *
 * Files are loaded in alphabetical order, so:
 * - 001_sLangServiceProvider.php loads first (priority: 1)
 * - 050_SomeOtherProvider.php loads second (priority: 50)
 * - sCommerceServiceProvider.php loads third (priority: 0 or not specified)
 * - etc.
 *
 * @package EvolutionCMS\Console\Packages
 */
class PackageCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'package:discover';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generate ServiceProviders for custom packages';
    /**
     * Path for custom providers
     * @var string
     */
    protected $configDir = EVO_CORE_PATH . 'custom/config/app/providers/';
    /**
     * Custom composer.json
     * @var string
     */
    protected $composer = EVO_CORE_PATH . 'custom/composer.json';
    /**
     * @var string
     */
    public $packagePath = '';

    /**
     * @var mixed|string
     */
    public $load_dir = '';

    /**
     * @var \DocumentParser|string
     */
    public $evo = '';

    /**
     * @var array
     */
    public $require = [];

    /**
     * PackageCommand constructor.
     */
    public function __construct()
    {
        parent::__construct();
        $this->evo = evo();
        $this->load_dir = $this->evo->getConfig('rb_base_dir');
    }

    /**
     * Execute the console command.
     *
     * @return void
     */
    public function handle()
    {
        if (!is_dir($this->configDir)) {
            mkdir($this->configDir, 0775, true);
        }
        if (!is_dir($this->configDir)) {
            $this->getOutput()->write('<error>ERROR CREATE CONFIG DIR</error>');
            exit();
        }

        if (file_exists($this->composer)) {
            $this->parseComposer($this->composer);
        }
        if (count($this->require) > 0) {
            $this->loadRequire();
        }
        unlink(EVO_CORE_PATH . 'storage/bootstrap/services.php');
    }

    /**
     * @param string $composer
     */
    public function parseComposer(string $composer)
    {
        $data = json_decode(file_get_contents($composer), true);
        if (isset($data['extra']['laravel']['providers'])) {
            // Get priorities if defined (default to empty array if not present)
            $priorities = $data['extra']['laravel']['priority'] ?? [];

            foreach ($data['extra']['laravel']['providers'] as $value) {
                // Get priority for this provider (default 0 if not specified)
                $priority = $priorities[$value] ?? 0;
                $this->process($value, $priority);
            }
        }
        if (isset($data['require'])) {
            foreach ($data['require'] as $key => $value) {
                $composer = EVO_CORE_PATH . 'vendor/' . $key . '/composer.json';
                $this->packagePath = EVO_CORE_PATH . 'vendor/' . $key . '/';
                if (file_exists($composer)) {
                    $this->checkRequired($composer);
                    $this->parseComposerServiceProvider($composer);
                }
            }
        }
        if (isset($data['autoload']['psr-4'])) {
            foreach ($data['autoload']['psr-4'] as $key => $value) {
                $composer = EVO_CORE_PATH . 'custom/' . $value . '/composer.json';
                $this->packagePath = EVO_CORE_PATH . 'custom/' . $value . '/';
                if (file_exists($composer)) {
                    $this->parseComposerServiceProvider($composer);
                }

            }
        }
    }

    /**
     * @param string $composer
     */
    public function parseComposerServiceProvider(string $composer)
    {
        $data = json_decode(file_get_contents($composer), true);
        if (isset($data['extra']['laravel']['providers']) && is_array($data['extra']['laravel']['providers'])) {
            // Get priorities if defined (default to empty array if not present)
            $priorities = $data['extra']['laravel']['priority'] ?? [];

            foreach ($data['extra']['laravel']['providers'] as $value) {
                // Get priority for this provider (default 0 if not specified)
                $priority = $priorities[$value] ?? 0;
                $this->process($value, $priority);
            }
        }
        if (isset($data['extra']['laravel']['files'])) {
            foreach ($data['extra']['laravel']['files'] as $copyArray) {
                $this->copyFiles($copyArray);
            }
        }
    }

    /**
     * Generate provider config file with optional priority prefix
     *
     * @param string $value Provider class name
     * @param int $priority Priority (1-999), used for load order. Lower numbers load first.
     */
    protected function process(string $value, int $priority = 0)
    {
        $arrNamespace = explode('\\', $value);
        $className = end($arrNamespace);

        // Add priority prefix if specified (001_, 002_, ..., 999_)
        if ($priority > 0 && $priority < 1000) {
            $prefix = str_pad($priority, 3, '0', STR_PAD_LEFT) . '_';
            $fileName = $prefix . $className . '.php';
            $fileContent = "<?php\n// Priority {$priority} - loads before other providers\nreturn " . $value . "::class;";

            // Remove old files with different prefixes for the same provider
            $patterns = [
                $this->configDir . $className . '.php',                    // without prefix
                $this->configDir . '*_' . $className . '.php',             // with any prefix
            ];

            foreach ($patterns as $pattern) {
                foreach (glob($pattern) as $oldFile) {
                    if ($oldFile !== $this->configDir . $fileName) {
                        @unlink($oldFile);
                        $this->getOutput()->write('<comment>Removed old ' . basename($oldFile) . ' (using priority prefix)</comment>');
                    }
                }
            }
        } else {
            $fileName = $className . '.php';
            $fileContent = "<?php \nreturn " . $value . "::class;";
        }

        if (file_put_contents($this->configDir . $fileName, $fileContent)) {
            $this->getOutput()->write('<info>' . $value . ($priority > 0 ? " (priority: {$priority})" : '') . '</info>');
        } else {
            $this->getOutput()->write('<error>Error create config for: ' . $value . '</error>');
        }
        $this->line('');
    }

    /**
     * @param array $copyArray
     */
    protected function copyFiles(array $copyArray)
    {
        File::copyDirectory($this->packagePath . $copyArray['source'], $this->load_dir . $copyArray['destination']);
    }

    protected function checkRequired($composer)
    {
        $composerArray = json_decode(file_get_contents($composer), true);
        if (isset($composerArray['require']) && is_array($composerArray['require'])) {
            foreach ($composerArray['require'] as $key => $item) {
                $this->require[] = $key;
            }
        }
    }

    protected function loadRequire()
    {
        foreach ($this->require as $require) {
            $composer = EVO_CORE_PATH . 'vendor/' . $require . '/composer.json';
            $this->packagePath = EVO_CORE_PATH . 'vendor/' . $require . '/';
            if (file_exists($composer)) {
                $this->parseComposerServiceProvider($composer);
            }
        }
    }
}
