<?php

declare(strict_types=1);

namespace PpPhp\Modules\PluginManager\Controllers;

use PpPhp\Core\Config;
use PpPhp\Core\PluginRegistry;
use PpPhp\Core\PluginOwnership;
use PpPhp\Core\Plugins\PluginInstaller;
use PpPhp\Core\Security;

class PluginManagerController
{
    /** Roles allowed to open the Plugin Manager at all. */
    private const ALLOWED = ['superadmin', 'developer'];

    public function __construct(private Config $config) {}

    /**
     * Main page – list all installed plugins.
     */
    public function index(): string
    {
        Security::RequireAuth();
        Security::RequireRoleOrAlert(
            self::ALLOWED,
            'The Plugin Manager is available to developers and administrators only.'
        );

        // Refresh registry
        $registry = PluginRegistry::all();
        $baseUrl  = $this->config->get('base_url');
        $assetsUrl = $this->config->get('assets_url');
        $user = $_SESSION['user'] ?? [];
        $isAdmin = Security::IsAdmin();
        $isOwner = Security::IsOwner();

        ob_start();
        include __DIR__ . '/../Views/index.php';
        return ob_get_clean();
    }

    /**
     * Developer guide — how to build plugins for the platform.
     */
    public function guide(): string
    {
        Security::RequireAuth();
        Security::RequireRoleOrAlert(
            self::ALLOWED,
            'The Plugin Manager is available to developers and administrators only.'
        );

        $baseUrl   = $this->config->get('base_url');
        $assetsUrl = $this->config->get('assets_url');

        ob_start();
        include __DIR__ . '/../Views/guide.php';
        return ob_get_clean();
    }

    /**
     * Upload a new plugin (ZIP).
     */
    public function upload(): void
    {
        Security::RequireAuth();
        Security::RequireRoleOrAlert(
            self::ALLOWED,
            'The Plugin Manager is available to developers and administrators only.'
        );

        $category = (string) ($_POST['category'] ?? 'View');
        if (!PluginInstaller::isValidCategory($category)) {
            $category = 'View';
        }
        if (!isset($_FILES['plugin_zip']) || $_FILES['plugin_zip']['error'] !== UPLOAD_ERR_OK) {
            $_SESSION['plugin_error'] = 'File upload failed.';
            redirect('/plugin-manager');
        }

        $res = PluginInstaller::install(
            (string) $_FILES['plugin_zip']['tmp_name'],
            $category,
            (int) (Security::CurrentUserData()['id'] ?? 0),
            Security::CurrentUsername()
        );
        $_SESSION[$res['ok'] ? 'plugin_success' : 'plugin_error'] = $res['message'];
        redirect('/plugin-manager');
    }

    /**
     * Update an existing plugin in place from a new ZIP. The previous version is
     * backed up and restored automatically if the new package is invalid. Core
     * plugins are owner-only (enforced by PluginInstaller via PluginOwnership).
     */
    public function update(): void
    {
        Security::RequireAuth();
        Security::RequireRoleOrAlert(
            self::ALLOWED,
            'The Plugin Manager is available to developers and administrators only.'
        );

        $category = (string) ($_POST['category'] ?? '');
        $name     = (string) ($_POST['name'] ?? '');
        if (!PluginInstaller::isValidCategory($category)) {
            $_SESSION['plugin_error'] = 'Unknown plugin category.';
            redirect('/plugin-manager');
        }
        if (!isset($_FILES['plugin_zip']) || $_FILES['plugin_zip']['error'] !== UPLOAD_ERR_OK) {
            $_SESSION['plugin_error'] = 'No update file received.';
            redirect('/plugin-manager');
        }

        $res = PluginInstaller::update(
            (string) $_FILES['plugin_zip']['tmp_name'],
            $category,
            $name,
            (int) (Security::CurrentUserData()['id'] ?? 0),
            Security::CurrentUsername()
        );
        $_SESSION[$res['ok'] ? 'plugin_success' : 'plugin_error'] = $res['message'];
        redirect('/plugin-manager');
    }

    /**
     * Owner-only: mark a plugin as Core (owner-managed) or Custom.
     */
    public function markCore(): void
    {
        Security::RequireAuth();
        if (!Security::IsOwner()) {
            $_SESSION['plugin_error'] = 'Only the platform owner can change a plugin’s Core/Custom status.';
            redirect('/plugin-manager');
        }
        $category = (string) ($_POST['category'] ?? '');
        $name = (string) preg_replace('/[^A-Za-z0-9_\-]/', '', (string) ($_POST['name'] ?? ''));
        $core = (string) ($_POST['core'] ?? '') === '1';
        if (!PluginInstaller::isValidCategory($category) || $name === '' || !is_dir(constant('BASE_PATH') . "/plugins/{$category}/{$name}")) {
            $_SESSION['plugin_error'] = 'Plugin not found.';
            redirect('/plugin-manager');
        }
        if (PluginOwnership::setCore($category, $name, $core)) {
            PluginRegistry::reset();
            $_SESSION['plugin_success'] = "“{$name}” is now a " . ($core ? 'Core' : 'Custom') . ' plugin.';
        } else {
            $_SESSION['plugin_error'] = 'Could not update the plugin status.';
        }
        redirect('/plugin-manager');
    }

    /**
     * Delete a plugin by its folder name.
     */
    public function delete(string $name): void
    {
        Security::RequireAuth();
        Security::RequireRoleOrAlert(
            self::ALLOWED,
            'The Plugin Manager is available to developers and administrators only.'
        );

        $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
        foreach (PluginInstaller::CATEGORIES as $cat) {
            if (!is_dir(constant('BASE_PATH') . "/plugins/{$cat}/{$name}")) {
                continue;
            }
            $res = PluginInstaller::remove($cat, (string) $name);
            $_SESSION[$res['ok'] ? 'plugin_success' : 'plugin_error'] = $res['message'];
            redirect('/plugin-manager');
        }
        $_SESSION['plugin_error'] = 'Plugin not found.';
        redirect('/plugin-manager');
    }

    /**
     * Toggle plugin enabled/disabled (stub – uses .disabled file).
     */
    public function toggle(string $name): void
    {
        Security::RequireAuth();
        Security::RequireRoleOrAlert(
            self::ALLOWED,
            'The Plugin Manager is available to developers and administrators only.'
        );

        $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
        $base = constant('BASE_PATH') . '/plugins/';
        foreach (PluginInstaller::CATEGORIES as $cat) {
            $path = $base . $cat . '/' . $name;
            if (!is_dir($path)) {
                continue;
            }
            if (!PluginOwnership::canManage($cat, $name)) {
                $_SESSION['plugin_error'] = self::notYoursMessage($cat, $name);
                redirect('/plugin-manager');
            }
            $flagFile = $path . '/.disabled';
            if (file_exists($flagFile)) {
                unlink($flagFile);
            } else {
                file_put_contents($flagFile, '1');
            }
            redirect('/plugin-manager');
        }
        redirect('/plugin-manager');
    }

    /** Explain, without blaming, why a developer can't touch this plugin. */
    private static function notYoursMessage(string $category, string $name): string
    {
        $owner = PluginOwnership::ownerLabel($category, $name);
        return "“{$name}” was installed by {$owner}, so only they or an administrator can change it. "
            . 'You can still install and manage your own plugins.';
    }

    private static function deleteDir(string $dir): void
    {
        if (!is_dir($dir)) return;
        foreach (scandir($dir) as $item) {
            if ($item === '.' || $item === '..') continue;
            $path = $dir . '/' . $item;
            is_dir($path) ? self::deleteDir($path) : unlink($path);
        }
        rmdir($dir);
    }
}