<?php

namespace Core\System;

use PDO;
use RuntimeException;

class PluginManager
{
    /**
     * تثبيت إضافة من ZIP
     */
    public static function installFromZip(array $file): string
    {
        if (empty($file['tmp_name']) || !is_uploaded_file($file['tmp_name'])) {
            throw new RuntimeException('invalid_plugin_upload');
        }

        $zip = new \ZipArchive();

        if ($zip->open($file['tmp_name']) !== true) {
            throw new RuntimeException('invalid_plugin_zip');
        }

        // اكتشاف الجذر
        $rootDir = null;
        for ($i = 0; $i < $zip->numFiles; $i++) {
            $name = $zip->getNameIndex($i);
            $parts = explode('/', $name);
            if (count($parts) > 1) {
                $rootDir = $parts[0];
                break;
            }
        }

        if (!$rootDir) {
            throw new RuntimeException('invalid_plugin_structure');
        }

        $target = ROOT_PATH . '/plugins/' . $rootDir;

        $manifestRaw = $zip->getFromName($rootDir . '/plugin.json');
        if ($manifestRaw === false) {
            $zip->close();
            throw new RuntimeException('plugin_manifest_missing');
        }

        $manifest = json_decode($manifestRaw, true);
        if (!is_array($manifest) || empty($manifest['slug'])) {
            $zip->close();
            throw new RuntimeException('invalid_plugin_manifest');
        }

        if (is_dir($target)) {
            $existingManifestPath = $target . '/plugin.json';
            $existingManifest = is_file($existingManifestPath)
                ? json_decode((string) file_get_contents($existingManifestPath), true)
                : null;

            if (!is_array($existingManifest) || (($existingManifest['slug'] ?? null) !== $manifest['slug'])) {
                $zip->close();
                throw new RuntimeException('plugin_already_exists');
            }

            self::deleteDirectory($target);
        }

        // Security: validate all ZIP entries before extraction (Zip Slip protection)
        $pluginsBase = realpath(ROOT_PATH . '/plugins') ?: (ROOT_PATH . '/plugins');
        $dangerousExts = ['php', 'php3', 'php4', 'php5', 'php7', 'php8', 'phtml', 'phar', 'cgi', 'pl', 'py', 'sh', 'exe', 'bat'];

        for ($i = 0; $i < $zip->numFiles; $i++) {
            $entryName = $zip->getNameIndex($i);

            // Block path traversal
            if (strpos($entryName, '..') !== false || strpos($entryName, './') !== false) {
                $zip->close();
                throw new RuntimeException('plugin_zip_path_traversal');
            }

            // Validate real extraction path stays inside plugins directory
            $extractedPath = realpath($pluginsBase . DIRECTORY_SEPARATOR . $entryName);
            if ($extractedPath !== false && strpos($extractedPath, $pluginsBase) !== 0) {
                $zip->close();
                throw new RuntimeException('plugin_zip_path_traversal');
            }

            // Block dangerous extensions outside expected structure
            // (Installer.php and routes.php are intentionally allowed for plugin functionality)
            $ext = strtolower(pathinfo($entryName, PATHINFO_EXTENSION));
            if (in_array($ext, $dangerousExts, true)) {
                // Only allow .php files that are direct children of the plugin root directory
                $parts = explode('/', trim($entryName, '/'));
                if (count($parts) < 2) {
                    $zip->close();
                    throw new RuntimeException('plugin_zip_dangerous_file');
                }
            }
        }

        $zip->extractTo(ROOT_PATH . '/plugins');
        $zip->close();

        $manifestPath = $target . '/plugin.json';

        if (!is_file($manifestPath)) {
            throw new RuntimeException('plugin_manifest_missing');
        }

        $manifest = json_decode(file_get_contents($manifestPath), true);

        if (!is_array($manifest) || empty($manifest['slug'])) {
            throw new RuntimeException('invalid_plugin_manifest');
        }

        db()->prepare("DELETE FROM plugins WHERE slug = ?")->execute([$manifest['slug']]);

        // تسجيل الإضافة في DB
        self::registerPlugin($manifest);

        // تشغيل Installer إن وجد
        self::runInstaller($manifest['slug'], 'install');

        return $manifest['slug'];
    }

    /**
     * تسجيل الإضافة في قاعدة البيانات
     */
    protected static function registerPlugin(array $manifest): void
    {
        $stmt = db()->prepare("
            INSERT INTO plugins
                (slug, name, version, description, is_active)
            VALUES
                (:slug, :name, :version, :description, 0)
        ");

        $stmt->execute([
            ':slug'        => $manifest['slug'],
            ':name'        => $manifest['name']        ?? $manifest['slug'],
            ':version'     => $manifest['version']     ?? '1.0.0',
            ':description' => $manifest['description'] ?? null,
        ]);
    }

    /**
     * تفعيل إضافة
     */
    public static function enable(string $slug): void
    {
        self::runInstaller($slug, 'enable');

        db()->prepare("
            UPDATE plugins
            SET is_active = 1,
                updated_at = NOW()
            WHERE slug = ?
        ")->execute([$slug]);
    }

    /**
     * تعطيل إضافة
     */
    public static function disable(string $slug): void
    {
        self::runInstaller($slug, 'disable');

        db()->prepare("
            UPDATE plugins
            SET is_active = 0,
                updated_at = NOW()
            WHERE slug = ?
        ")->execute([$slug]);
    }

    /**
     * تشغيل Installer الخاص بالإضافة
     */
    protected static function runInstaller(string $slug, string $method): void
    {
        $plugin = PluginRegistry::get($slug);

        if (!$plugin) {
            return;
        }

        $installerFile = $plugin['path'] . '/Installer.php';

        if (!is_file($installerFile)) {
            return;
        }

        require_once $installerFile;

        $class = $plugin['installer'] ?? null;

        if (!$class || !class_exists($class)) {
            return;
        }

        $instance = new $class();

        if (method_exists($instance, $method)) {
            $instance->{$method}();
        }
    }
    
    /**
 * تحميل جميع الإضافات المفعّلة
 */
public static function loadPlugins(): void
{
    $stmt = db()->query("
        SELECT slug
        FROM plugins
        WHERE is_active = 1
    ");

    $slugs = $stmt->fetchAll(PDO::FETCH_COLUMN);

foreach ($slugs as $slug) {

    $pluginPath = ROOT_PATH . '/plugins/' . self::slugToFolder($slug);

    if (!is_dir($pluginPath)) {
        continue;
    }

    // تحميل routes إن وجدت
    $routesFile = $pluginPath . '/routes.php';
    if (is_file($routesFile)) {
        require_once $routesFile;
    }
}

}
public static function isActive(string $slug): bool
{
    $stmt = db()->prepare("
        SELECT COUNT(*)
        FROM plugins
        WHERE slug = ? AND is_active = 1
        LIMIT 1
    ");
    $stmt->execute([$slug]);

    return (bool)$stmt->fetchColumn();
}

/**
 * جلب جميع الإضافات (مثبتة في DB)
 */
public static function all(): array
{
    $stmt = db()->query("
        SELECT
            slug,
            name,
            version,
            description,
            is_active
        FROM plugins
        ORDER BY name ASC
    ");

    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $plugins = [];

    foreach ($rows as $row) {
        $plugins[] = [
            'slug'        => $row['slug'],
            'name'        => $row['name'],
            'version'     => $row['version'],
            'description' => $row['description'],
            'enabled'     => (bool) $row['is_active'],
        ];
    }

    return $plugins;
}

protected static function slugToFolder(string $slug): string
{
    return str_replace(' ', '', ucwords(str_replace('-', ' ', $slug)));
}

protected static function deleteDirectory(string $dir): bool
{
    if (!is_dir($dir)) {
        return false;
    }

    $items = new \RecursiveIteratorIterator(
        new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
        \RecursiveIteratorIterator::CHILD_FIRST
    );

    foreach ($items as $item) {
        if ($item->isDir()) {
            @rmdir($item->getRealPath());
        } else {
            @unlink($item->getRealPath());
        }
    }

    return @rmdir($dir);
}

}
