<?php

declare(strict_types=1);

namespace Modules\Plugins\Commands;

use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use Exception;
use Modules\Plugins\Config\Plugins as PluginsConfig;
use Modules\Plugins\Core\Plugins;
use Modules\Plugins\Manifest\Manifest;
use Override;

class CreatePlugin extends BaseCommand
{
    protected const HOOKS_IMPORTS = [
        'rssBeforeChannel' => ['use App\Entities\Podcast;'],
        'rssAfterChannel'  => ['use App\Entities\Podcast;', 'use App\Libraries\SimpleRSSElement;'],
        'rssBeforeItem'    => ['use App\Entities\Episode;'],
        'rssAfterItem'     => ['use App\Entities\Episode;', 'use App\Libraries\SimpleRSSElement;'],
        'siteHead'         => [],
    ];

    protected const HOOKS_METHODS = [
        'rssBeforeChannel' => '    public function rssBeforeChannel(Podcast $podcast): void
    {
        // YOUR CODE HERE
    }',
        'rssAfterChannel' => '    public function rssAfterChannel(Podcast $podcast, SimpleRSSElement $channel): void
    {
        // YOUR CODE HERE
    }',
        'rssBeforeItem' => '    public function rssBeforeItem(Episode $episode): void
    {
        // YOUR CODE HERE
    }',
        'rssAfterItem' => '    public function rssAfterItem(Episode $episode, SimpleRSSElement $item): void
    {
        // YOUR CODE HERE
    }',
        'siteHead' => '    public function siteHead(): void
    {
        // YOUR CODE HERE
    }',
    ];

    /**
     * @var string
     */
    protected $group = 'Plugins';

    /**
     * @var string
     */
    protected $name = 'plugins:create';

    /**
     * @var string
     */
    protected $description = 'Generates a new plugin folder based on a template.';

    /**
     * Actually execute a command.
     *
     * @param list<string> $params
     */
    #[Override]
    public function run(array $params): void
    {
        $pluginName = CLI::prompt(
            'Plugin name (<vendor>/<name>)',
            'acme/hello-world',
            Manifest::VALIDATION_RULES['name']
        );
        CLI::newLine();
        $description = CLI::prompt('Description', '', Manifest::VALIDATION_RULES['description']);
        CLI::newLine();
        $license = CLI::prompt('License', 'UNLICENSED', Manifest::VALIDATION_RULES['license']);
        CLI::newLine();
        $hooks = CLI::promptByMultipleKeys('Which hooks do you want to implement?', Plugins::HOOKS);

        $nameParts = explode('/', $pluginName);
        $vendor = $nameParts[0];
        $name = $nameParts[1];

        /** @var PluginsConfig $pluginsConfig */
        $pluginsConfig = config('Plugins');

        // 1. create plugin directory if not existent
        $pluginDirectory = $pluginsConfig->folder . $vendor . DIRECTORY_SEPARATOR . $name;
        if (! file_exists($pluginDirectory)) {
            mkdir($pluginDirectory, 0755, true);
        }

        // 2. get contents of templates
        $manifestTemplate = file_get_contents(__DIR__ . '/plugin-template/manifest.tpl.json');

        if (! $manifestTemplate) {
            throw new Exception('Failed to get manifest template.');
        }

        $pluginClassTemplate = file_get_contents(__DIR__ . '/plugin-template/Plugin.tpl.php');

        if (! $pluginClassTemplate) {
            throw new Exception('Failed to get Plugin class template.');
        }

        // 3. edit templates' contents
        $manifestContents = str_replace('"name": ""', '"name": "' . $pluginName . '"', $manifestTemplate);
        $manifestContents = str_replace(
            '"description": ""',
            '"description": "' . $description . '"',
            $manifestContents
        );
        $manifestContents = str_replace('"license": ""', '"license": "' . $license . '"', $manifestContents);
        $manifestContents = str_replace(
            '"hooks": []',
            '"hooks": ["' . implode('", "', $hooks) . '"]',
            $manifestContents
        );

        $pluginClassName = str_replace(
            ' ',
            '',
            ucwords(str_replace(['-', '_', '.'], ' ', $vendor . ' ' . $name)) . 'Plugin'
        );
        $pluginClassContents = str_replace('class Plugin', 'class ' . $pluginClassName, $pluginClassTemplate);

        $allImports = [];
        $allMethods = [];
        foreach ($hooks as $hook) {
            $allImports = [...$allImports, ...self::HOOKS_IMPORTS[$hook]];
            $allMethods = [...$allMethods, self::HOOKS_METHODS[$hook]];
        }

        $imports = implode(PHP_EOL, array_unique($allImports));
        $methods = implode(PHP_EOL . PHP_EOL, $allMethods);
        $pluginClassContents = str_replace('// IMPORTS_HERE', $imports, $pluginClassContents);
        $pluginClassContents = str_replace('    // HOOKS_HERE', $methods, $pluginClassContents);

        $manifest = $pluginDirectory . '/manifest.json';
        $pluginClass = $pluginDirectory . '/Plugin.php';

        if (! file_put_contents($manifest, $manifestContents)) {
            throw new Exception('Failed to create manifest.json file.');
        }

        if (! file_put_contents($pluginClass, $pluginClassContents)) {
            throw new Exception('Failed to create Plugin class file.');
        }

        CLI::newLine(1);
        CLI::write(
            sprintf('Plugin %s created in %s', CLI::color($pluginName, 'white'), CLI::color($pluginDirectory, 'white')),
            'green'
        );
    }
}
