// @lifecycle canonical - Coordinates gate selection and orchestrates the gate system.
/**
 * Gate Manager
 *
 * Orchestration layer for the gate system.
 * Extends BaseResourceHandler to provide unified resource management patterns.
 *
 * Coordinates between:
 * - GateRegistry: Lifecycle management for gate guides
 * - GateStateStore: Runtime enable/disable state
 * - Gate selection and activation logic
 */

import { GateRegistry, createGateRegistry, type GateRegistryConfig } from './registry/index.js';
import { Logger } from '../../infra/logging/index.js';
import { BaseResourceHandler } from '../../shared/core/resource-manager/index.js';

import type { GateStateStore } from './gate-state-store.js';
import type {
  GateGuide,
  GateActivationContext,
  GateSelectionContext,
  GateSelectionResult,
  GateGuideEntry,
  GateRegistryStats,
} from './types/index.js';
import type { IGateManager } from './types.js';

/**
 * Configuration for GateManager
 */
export interface GateManagerConfig {
  /** Configuration for the underlying GateRegistry */
  registryConfig?: Partial<GateRegistryConfig>;
  /** Enable debug logging */
  debug?: boolean;
}

/**
 * Gate Manager
 *
 * Provides orchestration for the gate system, managing gate selection,
 * activation, and coordination with the state manager.
 *
 * @example
 * ```typescript
 * const manager = new GateManager(logger);
 * await manager.initialize();
 *
 * // Select gates for a context
 * const result = manager.selectGates({ promptCategory: 'code' });
 * for (const guide of result.guides) {
 *   console.log(guide.getGuidance());
 * }
 * ```
 */
export class GateManager
  extends BaseResourceHandler<GateGuide, GateGuideEntry, GateManagerConfig, GateRegistryStats>
  implements IGateManager
{
  private registry: GateRegistry | null = null;
  private stateManager: GateStateStore | null = null;

  constructor(logger: Logger, config: GateManagerConfig = {}) {
    super(logger, config);
  }

  // ============================================================================
  // BaseResourceHandler Abstract Method Implementations
  // ============================================================================

  protected get managerName(): string {
    return 'GateManager';
  }

  protected async initializeRegistry(): Promise<void> {
    this.registry = await createGateRegistry(this.logger, this.config.registryConfig);
    const stats = this.registry.getRegistryStats();
    this.logger.debug(`GateRegistry loaded with ${stats.totalGates} gates`);
  }

  protected applyDefaultConfig(config: GateManagerConfig): GateManagerConfig {
    return {
      registryConfig: config.registryConfig ?? {},
      debug: config.debug ?? false,
    };
  }

  protected getResource(id: string): GateGuide | undefined {
    return this.registry!.getGuide(id);
  }

  protected hasResource(id: string): boolean {
    return this.registry!.hasGuide(id);
  }

  protected listResources(enabledOnly: boolean): GateGuide[] {
    return this.registry!.getAllGuides(enabledOnly);
  }

  protected getResourceEntries(enabledOnly: boolean): GateGuideEntry[] {
    return this.registry!.getGuideEntries(enabledOnly);
  }

  protected setResourceEnabled(id: string, enabled: boolean): boolean {
    return this.registry!.setGuideEnabled(id, enabled);
  }

  protected async reloadResource(id: string): Promise<boolean> {
    return this.registry!.reloadGuide(id);
  }

  protected unregisterResource(id: string): boolean {
    return this.registry!.unregisterGuide(id);
  }

  protected clearResourceCache(id?: string): void {
    const loader = this.registry!.getLoader();
    loader.clearCache(id);
  }

  protected getResourceStats(): GateRegistryStats {
    return this.registry!.getRegistryStats();
  }

  protected override isSystemEnabled(): boolean {
    if (!this.stateManager) {
      return true; // Default to enabled if no state manager
    }
    return this.stateManager.isGateSystemEnabled();
  }

  // ============================================================================
  // Domain-Specific Methods (Gate Selection & Activation)
  // ============================================================================

  /**
   * Set the gate system state manager for synchronization
   */
  setStateManager(stateManager: GateStateStore): void {
    this.stateManager = stateManager;
    this.logger.debug('GateStateStore synchronized with GateManager');
  }

  /**
   * Check if the gate system is enabled
   */
  isGateSystemEnabled(): boolean {
    return this.isSystemEnabled();
  }

  /**
   * Select gates based on context criteria
   *
   * This is the primary method for getting gates that should be applied
   * to a given execution context.
   *
   * @param context - Selection context with category, framework, etc.
   * @returns Selection result with matched guides
   */
  selectGates(context: GateSelectionContext): GateSelectionResult {
    this.ensureInitialized();
    const startTime = performance.now();

    // If gate system is disabled, return empty result
    if (!this.isSystemEnabled()) {
      return {
        guides: [],
        selectedIds: [],
        skippedIds: [],
        metadata: {
          selectionMethod: 'explicit',
          selectionTime: performance.now() - startTime,
        },
      };
    }

    const selectedGuides: GateGuide[] = [];
    const selectedIds: string[] = [];
    const skippedIds: string[] = [];

    // Build activation context from selection context
    const activationContext: GateActivationContext = { explicitRequest: false };
    if (context.promptCategory) {
      activationContext.promptCategory = context.promptCategory;
    }
    if (context.framework) {
      activationContext.framework = context.framework;
    }

    // Get all enabled guides
    const allGuides = this.registry!.getAllGuides(context.enabledOnly ?? true);

    for (const guide of allGuides) {
      // Check if explicitly requested
      if (context.explicitGateIds?.includes(guide.gateId)) {
        selectedGuides.push(guide);
        selectedIds.push(guide.gateId);
        continue;
      }

      // Check activation rules
      if (guide.isActive(activationContext)) {
        selectedGuides.push(guide);
        selectedIds.push(guide.gateId);
      } else {
        skippedIds.push(guide.gateId);
      }
    }

    // Determine selection method
    let selectionMethod: 'explicit' | 'category' | 'framework' | 'combined' = 'combined';
    if (context.explicitGateIds?.length && !context.promptCategory && !context.framework) {
      selectionMethod = 'explicit';
    } else if (context.promptCategory && !context.framework) {
      selectionMethod = 'category';
    } else if (context.framework && !context.promptCategory) {
      selectionMethod = 'framework';
    }

    if (this.config.debug) {
      this.logger.debug(
        `Gate selection: ${selectedIds.length} selected, ${skippedIds.length} skipped [${selectionMethod}]`
      );
    }

    return {
      guides: selectedGuides,
      selectedIds,
      skippedIds,
      metadata: {
        selectionMethod,
        selectionTime: performance.now() - startTime,
      },
    };
  }

  /**
   * Get active gates for specific gate IDs in a given context
   *
   * @param gateIds - Gate IDs to check
   * @param context - Activation context
   * @returns Array of active gate guides
   */
  getActiveGates(gateIds: string[], context: GateActivationContext): GateGuide[] {
    this.ensureInitialized();

    if (!this.isSystemEnabled()) {
      return [];
    }

    const activeGuides: GateGuide[] = [];

    for (const gateId of gateIds) {
      const guide = this.registry!.getGuide(gateId);
      if (guide?.isActive(context)) {
        activeGuides.push(guide);
      }
    }

    return activeGuides;
  }

  /**
   * Get the underlying gate registry
   */
  getGateRegistry(): GateRegistry {
    this.ensureInitialized();
    return this.registry!;
  }

  /**
   * Get gates that auto-activate for a given category.
   *
   * Used by ExecutionPlanner to replace hardcoded autoAssignGates().
   * This method queries all gates and filters by their activation.prompt_categories
   * rules, excluding framework gates (which are handled separately via framework_gates flag).
   *
   * @param category - The prompt category (e.g., 'development', 'research', 'analysis')
   * @returns Array of gate IDs that should auto-activate for this category
   */
  getCategoryGates(category: string): string[] {
    this.ensureInitialized();

    // If gate system is disabled, return empty
    if (!this.isSystemEnabled()) {
      return [];
    }

    const normalizedCategory = category.length > 0 ? category.toLowerCase() : 'general';

    const activationContext: GateActivationContext = {
      promptCategory: normalizedCategory,
      explicitRequest: false,
    };

    // Get all enabled guides
    const allGuides = this.registry!.getAllGuides(true);

    const categoryGates = allGuides
      .filter((guide) => {
        // Skip framework gates - they're handled separately via framework_gates flag
        if (guide.gateType === 'framework') {
          return false;
        }
        // Check if gate activates for this category
        return guide.isActive(activationContext);
      })
      .map((guide) => guide.gateId);

    if (this.config.debug === true) {
      const gateList = categoryGates.length > 0 ? categoryGates.join(', ') : '(none)';
      this.logger.debug(`[GateManager] getCategoryGates('${normalizedCategory}'): ${gateList}`);
    }

    return categoryGates;
  }

  /**
   * Get combined gate system status
   *
   * Overrides base class to include domain-specific fields
   */
  override getStatus(): {
    enabled: boolean;
    initialized: boolean;
    registryStats: GateRegistryStats | null;
    stateManagerConnected: boolean;
  } {
    return {
      enabled: this.isSystemEnabled(),
      initialized: this.initialized,
      registryStats: this.initialized ? this.registry!.getRegistryStats() : null,
      stateManagerConnected: this.stateManager !== null,
    };
  }
}

/**
 * Create and initialize a GateManager
 */
export async function createGateManager(
  logger: Logger,
  config?: GateManagerConfig
): Promise<GateManager> {
  const manager = new GateManager(logger, config);
  await manager.initialize();
  return manager;
}
