<?php
declare(strict_types=1);

namespace Celena\Module\Plugins;

use Celena\Core\Database\Connection;
use Celena\Core\Http\Request;
use Celena\Core\Http\Response;
use Celena\Core\Kernel;
use Celena\Core\Logger;
use Celena\Core\Plugin\Installer;
use Celena\Core\Plugin\PluginManager;
use Celena\Core\Security\Auth;
use Celena\Core\Security\Csrf;
use Celena\Core\Security\Permission;
use Celena\Core\Template\Engine;
use Celena\Core\Template\ThemeManager;
use Celena\Module\AdminBase\AdminController;
use Celena\Module\Settings\Options;

final class PluginsController extends AdminController
{
    public function __construct(
        Engine $engine,
        Auth $auth,
        Options $options,
        private readonly PluginManager $plugins,
        private readonly ThemeManager $themes,
    ) {
        parent::__construct($engine, $auth, $options);
    }

    public function index(Request $request): Response
    {
        $this->plugins->discover();
        $installed = $this->plugins->all();
        $active = json_decode((string) $this->options->get('active_plugins', '[]'), true) ?: [];
        $list = [];
        foreach ($installed as $slug => $m) {
            $m['is_active'] = in_array($slug, is_array($active) ? $active : [], true) ? '1' : '0';
            $list[] = $m;
        }
        return $this->view('admin/plugins/index', [
            'plugins' => $list,
        ], 'plugins', 'Плагины');
    }

    public function templates(Request $request): Response
    {
        return $this->view('admin/templates-ui/index', [
            'themes' => $this->themes->all(),
            'active_theme' => (string) $this->options->get('theme', 'default'),
        ], 'templates', 'Шаблоны');
    }

    public function upload(Request $request): Response
    {
        if (!Permission::can($this->auth->user(), 'plugins.install')) {
            return $this->redirect('/admin/plugins', 'Доступ запрещён.', 'error');
        }
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/plugins', 'CSRF.', 'error');
        }
        $file = $request->file('plugin_zip');
        if ($file === null || ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
            return $this->redirect('/admin/plugins', 'Загрузите zip-файл.', 'error');
        }
        try {
            $installer = new Installer(
                Kernel::rootPath() . '/plugins',
                Kernel::container()->make(Connection::class),
                Kernel::container()->make(Logger::class),
            );
            $result = $installer->installFromZip((string) $file['tmp_name']);
            return $this->redirect('/admin/plugins', 'Плагин установлен: ' . $result['slug']);
        } catch (\Throwable $e) {
            return $this->redirect('/admin/plugins', 'Ошибка: ' . $e->getMessage(), 'error');
        }
    }

    public function toggle(Request $request, array $params): Response
    {
        if (!Permission::can($this->auth->user(), 'plugins.toggle')) {
            return $this->redirect('/admin/plugins', 'Доступ запрещён.', 'error');
        }
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/plugins', 'CSRF.', 'error');
        }
        $slug = (string) ($params['slug'] ?? '');
        $active = json_decode((string) $this->options->get('active_plugins', '[]'), true) ?: [];
        $active = is_array($active) ? $active : [];
        if (in_array($slug, $active, true)) {
            $active = array_values(array_filter($active, fn ($s) => $s !== $slug));
            $msg = 'Плагин отключён: ' . $slug;
        } else {
            $this->plugins->discover();
            $all = $this->plugins->all();
            if (!isset($all[$slug])) {
                return $this->redirect('/admin/plugins', 'Плагин не найден.', 'error');
            }
            // Применяем миграции плагина при первой активации.
            $migrationsDir = (string) $all[$slug]['path'] . '/migrations';
            if (is_dir($migrationsDir)) {
                try {
                    $runner = new \Celena\Core\Database\MigrationRunner(
                        Kernel::container()->make(Connection::class),
                        Kernel::container()->make(Logger::class),
                    );
                    $runner->run($migrationsDir);
                } catch (\Throwable $e) {
                    return $this->redirect('/admin/plugins', 'Ошибка миграций: ' . $e->getMessage(), 'error');
                }
            }
            $active[] = $slug;
            $msg = 'Плагин включён: ' . $slug;
        }
        $this->options->set('active_plugins', json_encode($active));
        return $this->redirect('/admin/plugins', $msg);
    }

    public function delete(Request $request, array $params): Response
    {
        if (!Permission::can($this->auth->user(), 'plugins.install')) {
            return $this->redirect('/admin/plugins', 'Доступ запрещён.', 'error');
        }
        if (!Csrf::validate((string) $request->input('_csrf'))) {
            return $this->redirect('/admin/plugins', 'CSRF.', 'error');
        }
        $slug = preg_replace('/[^a-z0-9_\-]/', '', (string) ($params['slug'] ?? '')) ?? '';
        if ($slug === '') {
            return $this->redirect('/admin/plugins', 'Неверный slug.', 'error');
        }
        $dir = Kernel::rootPath() . '/plugins/' . $slug;
        if (is_dir($dir)) {
            $this->rmdir($dir);
        }
        $active = json_decode((string) $this->options->get('active_plugins', '[]'), true) ?: [];
        $active = array_values(array_filter(is_array($active) ? $active : [], fn ($s) => $s !== $slug));
        $this->options->set('active_plugins', json_encode($active));
        return $this->redirect('/admin/plugins', 'Плагин удалён.');
    }

    private function rmdir(string $dir): void
    {
        if (!is_dir($dir)) return;
        $it = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::CHILD_FIRST
        );
        foreach ($it as $item) {
            $item->isDir() ? @rmdir($item->getPathname()) : @unlink($item->getPathname());
        }
        @rmdir($dir);
    }
}
