/**
 * Plugin Service - Manages frontend plugins for DSS Admin UI
 *
 * Supports loading, enabling/disabling, and configuring plugins.
 * Plugins can extend UI, add commands, and integrate with services.
 */

import logger from '../core/logger.js';

class PluginService {
  constructor() {
    this.plugins = new Map();
    this.hooks = new Map();
    this.loadedPlugins = [];
    this.storageKey = 'dss_plugins_config';

    // Load saved plugin states
    this.config = this._loadConfig();
  }

  /**
   * Load plugin configuration from localStorage
   */
  _loadConfig() {
    try {
      const saved = localStorage.getItem(this.storageKey);
      return saved ? JSON.parse(saved) : { enabled: {}, settings: {} };
    } catch (e) {
      logger.warn('PluginService', 'Failed to load plugin config', e);
      return { enabled: {}, settings: {} };
    }
  }

  /**
   * Save plugin configuration to localStorage
   */
  _saveConfig() {
    try {
      localStorage.setItem(this.storageKey, JSON.stringify(this.config));
    } catch (e) {
      logger.warn('PluginService', 'Failed to save plugin config', e);
    }
  }

  /**
   * Register a plugin
   * @param {Object} plugin - Plugin definition
   */
  register(plugin) {
    if (!plugin.id || !plugin.name) {
      throw new Error('Plugin must have id and name');
    }

    if (this.plugins.has(plugin.id)) {
      logger.warn('PluginService', `Plugin ${plugin.id} already registered`);
      return;
    }

    const pluginDef = {
      id: plugin.id,
      name: plugin.name,
      version: plugin.version || '1.0.0',
      description: plugin.description || '',
      author: plugin.author || 'Unknown',
      icon: plugin.icon || '🔌',
      category: plugin.category || 'general',

      // Lifecycle hooks
      onInit: plugin.onInit || (() => {}),
      onEnable: plugin.onEnable || (() => {}),
      onDisable: plugin.onDisable || (() => {}),
      onDestroy: plugin.onDestroy || (() => {}),

      // Extension points
      commands: plugin.commands || [],
      panels: plugin.panels || [],
      settings: plugin.settings || [],

      // State
      enabled: this.config.enabled[plugin.id] ?? plugin.enabledByDefault ?? false,
      initialized: false
    };

    this.plugins.set(plugin.id, pluginDef);
    logger.info('PluginService', `Registered plugin: ${plugin.name} v${pluginDef.version}`);

    // Auto-init if enabled
    if (pluginDef.enabled) {
      this._initPlugin(pluginDef);
    }

    return pluginDef;
  }

  /**
   * Initialize a plugin
   */
  async _initPlugin(plugin) {
    if (plugin.initialized) return;

    try {
      await plugin.onInit(this._createPluginContext(plugin));
      plugin.initialized = true;
      this.loadedPlugins.push(plugin.id);
      logger.info('PluginService', `Initialized plugin: ${plugin.name}`);
    } catch (e) {
      logger.error('PluginService', `Failed to init plugin ${plugin.name}`, e);
    }
  }

  /**
   * Create context object passed to plugin hooks
   */
  _createPluginContext(plugin) {
    return {
      pluginId: plugin.id,
      settings: this.config.settings[plugin.id] || {},

      // API for plugins
      registerHook: (hookName, callback) => this.registerHook(plugin.id, hookName, callback),
      unregisterHook: (hookName) => this.unregisterHook(plugin.id, hookName),
      getSettings: () => this.config.settings[plugin.id] || {},
      setSetting: (key, value) => this.setPluginSetting(plugin.id, key, value),

      // Access to app APIs
      emit: (event, data) => this._emitEvent(plugin.id, event, data),
      log: (msg, data) => logger.info(`Plugin:${plugin.name}`, msg, data)
    };
  }

  /**
   * Enable a plugin
   */
  async enable(pluginId) {
    const plugin = this.plugins.get(pluginId);
    if (!plugin) {
      throw new Error(`Plugin ${pluginId} not found`);
    }

    if (plugin.enabled) return;

    plugin.enabled = true;
    this.config.enabled[pluginId] = true;
    this._saveConfig();

    if (!plugin.initialized) {
      await this._initPlugin(plugin);
    }

    try {
      await plugin.onEnable(this._createPluginContext(plugin));
      logger.info('PluginService', `Enabled plugin: ${plugin.name}`);
      this._emitEvent('system', 'plugin:enabled', { pluginId });
    } catch (e) {
      logger.error('PluginService', `Failed to enable plugin ${plugin.name}`, e);
      plugin.enabled = false;
      this.config.enabled[pluginId] = false;
      this._saveConfig();
      throw e;
    }
  }

  /**
   * Disable a plugin
   */
  async disable(pluginId) {
    const plugin = this.plugins.get(pluginId);
    if (!plugin) {
      throw new Error(`Plugin ${pluginId} not found`);
    }

    if (!plugin.enabled) return;

    try {
      await plugin.onDisable(this._createPluginContext(plugin));
      plugin.enabled = false;
      this.config.enabled[pluginId] = false;
      this._saveConfig();
      logger.info('PluginService', `Disabled plugin: ${plugin.name}`);
      this._emitEvent('system', 'plugin:disabled', { pluginId });
    } catch (e) {
      logger.error('PluginService', `Failed to disable plugin ${plugin.name}`, e);
      throw e;
    }
  }

  /**
   * Toggle plugin enabled state
   */
  async toggle(pluginId) {
    const plugin = this.plugins.get(pluginId);
    if (!plugin) {
      throw new Error(`Plugin ${pluginId} not found`);
    }

    if (plugin.enabled) {
      await this.disable(pluginId);
    } else {
      await this.enable(pluginId);
    }
  }

  /**
   * Register a hook callback
   */
  registerHook(pluginId, hookName, callback) {
    const key = `${pluginId}:${hookName}`;
    if (!this.hooks.has(hookName)) {
      this.hooks.set(hookName, new Map());
    }
    this.hooks.get(hookName).set(pluginId, callback);
    logger.debug('PluginService', `Registered hook ${hookName} for ${pluginId}`);
  }

  /**
   * Unregister a hook callback
   */
  unregisterHook(pluginId, hookName) {
    const hookMap = this.hooks.get(hookName);
    if (hookMap) {
      hookMap.delete(pluginId);
    }
  }

  /**
   * Execute all callbacks for a hook
   */
  async executeHook(hookName, data = {}) {
    const hookMap = this.hooks.get(hookName);
    if (!hookMap) return [];

    const results = [];
    for (const [pluginId, callback] of hookMap) {
      const plugin = this.plugins.get(pluginId);
      if (plugin?.enabled) {
        try {
          const result = await callback(data);
          results.push({ pluginId, result });
        } catch (e) {
          logger.error('PluginService', `Hook ${hookName} failed for ${pluginId}`, e);
        }
      }
    }
    return results;
  }

  /**
   * Set a plugin setting
   */
  setPluginSetting(pluginId, key, value) {
    if (!this.config.settings[pluginId]) {
      this.config.settings[pluginId] = {};
    }
    this.config.settings[pluginId][key] = value;
    this._saveConfig();
    this._emitEvent(pluginId, 'settings:changed', { key, value });
  }

  /**
   * Get plugin settings
   */
  getPluginSettings(pluginId) {
    return this.config.settings[pluginId] || {};
  }

  /**
   * Emit event to listeners
   */
  _emitEvent(source, event, data) {
    window.dispatchEvent(new CustomEvent('dss:plugin:event', {
      detail: { source, event, data }
    }));
  }

  /**
   * Get all registered plugins
   */
  getAll() {
    return Array.from(this.plugins.values());
  }

  /**
   * Get enabled plugins
   */
  getEnabled() {
    return this.getAll().filter(p => p.enabled);
  }

  /**
   * Get plugin by ID
   */
  get(pluginId) {
    return this.plugins.get(pluginId);
  }

  /**
   * Get all commands from enabled plugins
   */
  getAllCommands() {
    const commands = [];
    for (const plugin of this.getEnabled()) {
      for (const cmd of plugin.commands) {
        commands.push({
          ...cmd,
          pluginId: plugin.id,
          pluginName: plugin.name
        });
      }
    }
    return commands;
  }

  /**
   * Get all panels from enabled plugins
   */
  getAllPanels() {
    const panels = [];
    for (const plugin of this.getEnabled()) {
      for (const panel of plugin.panels) {
        panels.push({
          ...panel,
          pluginId: plugin.id,
          pluginName: plugin.name
        });
      }
    }
    return panels;
  }
}

// Singleton instance
const pluginService = new PluginService();

export default pluginService;
export { PluginService };
