<?php

namespace App\Domain\PluginSdk\Services;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class PluginRegistry
{
    /**
     * Register or update a plugin manifest.
     *
     * @param  string  $slug  Unique machine-readable identifier, e.g. 'onox-wptoolkit'
     * @param  array  $manifest  Keys: name, version, author, description, hooks, sidebar_items, settings_schema, etc.
     */
    public static function register(string $slug, array $manifest): void
    {
        DB::table('installed_plugins')->updateOrInsert(
            ['slug' => $slug],
            [
                'name' => $manifest['name'] ?? $slug,
                'version' => $manifest['version'] ?? '0.1.0',
                'author' => $manifest['author'] ?? null,
                'description' => $manifest['description'] ?? null,
                'manifest' => json_encode($manifest),
                'enabled' => $manifest['enabled'] ?? true,
                'installed_at' => now(),
                'updated_at' => now(),
            ]
        );
    }

    /**
     * Return all enabled plugins as a Collection.
     */
    public static function listEnabled(): Collection
    {
        return DB::table('installed_plugins')
            ->where('enabled', true)
            ->get()
            ->map(function ($row) {
                $row->manifest = json_decode($row->manifest, true);
                $row->config = $row->config ? json_decode($row->config, true) : null;

                return $row;
            });
    }

    /**
     * Invoke a hook across all enabled plugins that declare it.
     *
     * Plugins may declare hooks in their manifest as:
     *   'hooks' => ['before_domain_create' => 'App\\Plugins\\MyPlugin@beforeDomainCreate']
     *
     * @return mixed The (possibly modified) payload after all hooks ran
     */
    public static function callHook(string $hookName, mixed $payload): mixed
    {
        $plugins = static::listEnabled();

        foreach ($plugins as $plugin) {
            $hooks = $plugin->manifest['hooks'] ?? [];

            if (isset($hooks[$hookName])) {
                $handler = $hooks[$hookName];

                if (is_callable($handler)) {
                    $payload = $handler($payload);
                } elseif (is_string($handler) && str_contains($handler, '@')) {
                    [$class, $method] = explode('@', $handler, 2);
                    if (class_exists($class) && method_exists($class, $method)) {
                        $payload = app($class)->{$method}($payload);
                    }
                }
            }
        }

        return $payload;
    }

    /**
     * Collect sidebar items declared by enabled plugins for a given context.
     *
     * @param  string  $context  'admin' or 'customer'
     * @return array Array of sidebar item definitions: [name, href, icon]
     */
    public static function discoverSidebarItems(string $context): array
    {
        $items = [];
        $plugins = static::listEnabled();

        foreach ($plugins as $plugin) {
            $sidebarItems = $plugin->manifest['sidebar_items'][$context] ?? [];
            foreach ($sidebarItems as $item) {
                $items[] = $item;
            }
        }

        return $items;
    }

    /**
     * Enable or disable a plugin by slug.
     */
    public static function setEnabled(string $slug, bool $enabled): void
    {
        DB::table('installed_plugins')
            ->where('slug', $slug)
            ->update(['enabled' => $enabled, 'updated_at' => now()]);
    }

    /**
     * Update plugin config (JSON blob, merged with existing).
     */
    public static function updateConfig(string $slug, array $config): void
    {
        $row = DB::table('installed_plugins')->where('slug', $slug)->first();
        if (! $row) {
            return;
        }

        $existing = $row->config ? json_decode($row->config, true) : [];
        $merged = array_merge($existing, $config);

        DB::table('installed_plugins')
            ->where('slug', $slug)
            ->update(['config' => json_encode($merged), 'updated_at' => now()]);
    }
}
