/**
 * [PLUGIN-002] 功能模块注册中心
 * 
 * 实现插拔式功能管理:
 * - 动态注册/注销功能模块
 * - 基于订阅计划自动启用/禁用
 * - 依赖检查和冲突检测
 * - 统一路由和菜单生成
 * 
 * @module plugins/FeatureRegistry
 * @created 2026-03-21
 */

import {
  FeatureConfig,
  FeatureState,
  FeatureRegistryOptions,
  IFeatureRegistry,
  FeatureRoute,
  FeatureMenuItem,
  FeaturePermission,
  FEATURE_EVENTS,
} from './FeatureTypes';

type EventCallback = (data: any) => void;

class FeatureRegistryImpl implements IFeatureRegistry {
  private features: Map<string, FeatureConfig> = new Map();
  private states: Map<string, FeatureState> = new Map();
  private eventListeners: Map<string, Set<EventCallback>> = new Map();
  private options: FeatureRegistryOptions;

  constructor(options: FeatureRegistryOptions = {}) {
    this.options = {
      strictMode: false,
      autoEnable: true,
      logLevel: 'info',
      ...options,
    };
  }

  register(feature: FeatureConfig): void {
    if (this.features.has(feature.id)) {
      if (this.options.strictMode) {
        throw new Error(`Feature ${feature.id} is already registered`);
      }
      console.warn(`Feature ${feature.id} is already registered, overwriting`);
    }

    const depCheck = this.checkDependencies(feature.id);
    if (!depCheck.satisfied) {
      console.warn(`Feature ${feature.id} has missing dependencies:`, depCheck.missing);
    }

    const conflictCheck = this.checkConflicts(feature.id);
    if (conflictCheck.hasConflicts) {
      console.warn(`Feature ${feature.id} has conflicts:`, conflictCheck.conflicts);
    }

    this.features.set(feature.id, feature);

    const existingState = this.states.get(feature.id);
    this.states.set(feature.id, {
      enabled: !!(this.options.autoEnable && depCheck.satisfied && !conflictCheck.hasConflicts),
      installed: true,
      version: feature.version,
      settings: this.getDefaultSettings(feature),
      installedAt: existingState?.installedAt || new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    });

    this.emit(FEATURE_EVENTS.FEATURE_REGISTERED, { featureId: feature.id });
  }

  unregister(featureId: string): void {
    const feature = this.features.get(featureId);
    if (!feature) return;

    const state = this.states.get(featureId);
    if (state?.enabled) {
      this.disableFeature(featureId);
    }

    this.features.delete(featureId);
    this.states.delete(featureId);

    this.emit(FEATURE_EVENTS.FEATURE_UNREGISTERED, { featureId });
  }

  getFeature(featureId: string): FeatureConfig | undefined {
    return this.features.get(featureId);
  }

  getFeatureState(featureId: string): FeatureState | undefined {
    return this.states.get(featureId);
  }

  getAllFeatures(): FeatureConfig[] {
    return Array.from(this.features.values());
  }

  getEnabledFeatures(): FeatureConfig[] {
    return this.getAllFeatures().filter(f => this.isFeatureEnabled(f.id));
  }

  getFeaturesByCategory(category: string): FeatureConfig[] {
    return this.getAllFeatures().filter(f => f.category === category);
  }

  async enableFeature(featureId: string): Promise<boolean> {
    const feature = this.features.get(featureId);
    if (!feature) return false;

    const state = this.states.get(featureId);
    if (!state || state.enabled) return false;

    const depCheck = this.checkDependencies(featureId);
    if (!depCheck.satisfied) {
      console.error(`Cannot enable ${featureId}: missing dependencies`, depCheck.missing);
      return false;
    }

    const conflictCheck = this.checkConflicts(featureId);
    if (conflictCheck.hasConflicts) {
      console.error(`Cannot enable ${featureId}: has conflicts`, conflictCheck.conflicts);
      return false;
    }

    try {
      if (feature.hooks?.onEnable) {
        await feature.hooks.onEnable();
      }

      state.enabled = true;
      state.updatedAt = new Date().toISOString();
      this.states.set(featureId, state);

      this.emit(FEATURE_EVENTS.FEATURE_ENABLED, { featureId });
      return true;
    } catch (error) {
      console.error(`Failed to enable feature ${featureId}:`, error);
      return false;
    }
  }

  async disableFeature(featureId: string): Promise<boolean> {
    const feature = this.features.get(featureId);
    if (!feature) return false;

    const state = this.states.get(featureId);
    if (!state || !state.enabled) return false;

    try {
      if (feature.hooks?.onDisable) {
        await feature.hooks.onDisable();
      }

      state.enabled = false;
      state.updatedAt = new Date().toISOString();
      this.states.set(featureId, state);

      this.emit(FEATURE_EVENTS.FEATURE_DISABLED, { featureId });
      return true;
    } catch (error) {
      console.error(`Failed to disable feature ${featureId}:`, error);
      return false;
    }
  }

  isFeatureEnabled(featureId: string): boolean {
    const state = this.states.get(featureId);
    return state?.enabled ?? false;
  }

  checkDependencies(featureId: string): { satisfied: boolean; missing: string[] } {
    const feature = this.features.get(featureId);
    if (!feature?.dependencies) return { satisfied: true, missing: [] };

    const missing: string[] = [];
    for (const dep of feature.dependencies) {
      const depFeature = this.features.get(dep.featureId);
      if (!depFeature || !this.isFeatureEnabled(dep.featureId)) {
        if (dep.required !== false) {
          missing.push(dep.featureId);
        }
      }
    }

    return { satisfied: missing.length === 0, missing };
  }

  checkConflicts(featureId: string): { hasConflicts: boolean; conflicts: string[] } {
    const feature = this.features.get(featureId);
    if (!feature?.conflicts) return { hasConflicts: false, conflicts: [] };

    const conflicts: string[] = [];
    for (const conflictId of feature.conflicts) {
      if (this.isFeatureEnabled(conflictId)) {
        conflicts.push(conflictId);
      }
    }

    return { hasConflicts: conflicts.length > 0, conflicts };
  }

  getRoutes(): FeatureRoute[] {
    const routes: FeatureRoute[] = [];
    for (const feature of this.getEnabledFeatures()) {
      if (feature.routes) {
        routes.push(...feature.routes);
      }
    }
    return routes;
  }

  getMenus(): FeatureMenuItem[] {
    const menus: FeatureMenuItem[] = [];
    for (const feature of this.getEnabledFeatures()) {
      if (feature.menus) {
        menus.push(...feature.menus);
      }
    }
    return menus;
  }

  getPermissions(): FeaturePermission[] {
    const permissions: FeaturePermission[] = [];
    for (const feature of this.getEnabledFeatures()) {
      if (feature.permissions) {
        permissions.push(...feature.permissions);
      }
    }
    return permissions;
  }

  updateSettings(featureId: string, settings: Record<string, any>): void {
    const state = this.states.get(featureId);
    if (!state) return;

    state.settings = { ...state.settings, ...settings };
    state.updatedAt = new Date().toISOString();
    this.states.set(featureId, state);

    this.emit(FEATURE_EVENTS.SETTINGS_CHANGED, { featureId, settings });
  }

  getSettings(featureId: string): Record<string, any> {
    return this.states.get(featureId)?.settings ?? {};
  }

  private getDefaultSettings(feature: FeatureConfig): Record<string, any> {
    const defaults: Record<string, any> = {};
    if (feature.settings) {
      for (const setting of feature.settings) {
        defaults[setting.key] = setting.defaultValue;
      }
    }
    return defaults;
  }

  on(event: string, callback: EventCallback): void {
    if (!this.eventListeners.has(event)) {
      this.eventListeners.set(event, new Set());
    }
    this.eventListeners.get(event)!.add(callback);
  }

  off(event: string, callback: EventCallback): void {
    this.eventListeners.get(event)?.delete(callback);
  }

  private emit(event: string, data: any): void {
    this.eventListeners.get(event)?.forEach(callback => {
      try {
        callback(data);
      } catch (error) {
        console.error(`Error in event listener for ${event}:`, error);
      }
    });
  }
}

export const featureRegistry = new FeatureRegistryImpl();

export { FeatureRegistryImpl };
