// @lifecycle canonical - Loads gate guides and tracks their registration state.
/**
 * Gate Registry
 *
 * Centralized registry for loading and managing gate guides.
 * Mirrors the MethodologyRegistry pattern from the framework system.
 *
 * Features:
 * - YAML-based loading via GateDefinitionLoader
 * - Per-gate enable/disable
 * - Registration metadata tracking
 * - Statistics and introspection
 *
 * @see MethodologyRegistry for the pattern this follows
 */

import { createGenericGateGuide } from './generic-gate-guide.js';
import { Logger } from '../../../infra/logging/index.js';
import {
  GateDefinitionLoader,
  type GateDefinitionLoaderConfig,
} from '../core/gate-definition-loader.js';

import type {
  GateDefinitionYaml,
  GateGuide,
  GateSource,
  GateGuideEntry,
  GateRegistryStats,
} from '../types/index.js';

/**
 * Gate registry configuration
 */
export interface GateRegistryConfig {
  /** Whether to auto-load built-in gates on initialization */
  autoLoadBuiltIn: boolean;
  /** Custom gate guides to load */
  customGuides?: GateGuide[];
  /** Whether to validate guides on registration */
  validateOnRegistration: boolean;
  /** Configuration for the gate definition loader */
  loaderConfig?: Partial<GateDefinitionLoaderConfig>;
}

/**
 * Gate Registry
 *
 * Manages the loading, registration, and lifecycle of gate guides.
 * Provides a clean separation between guide management and gate orchestration.
 *
 * @example
 * ```typescript
 * const registry = new GateRegistry(logger);
 * await registry.initialize();
 *
 * const guide = registry.getGuide('code-quality');
 * if (guide?.isActive({ promptCategory: 'code' })) {
 *   console.log(guide.getGuidance());
 * }
 * ```
 */
export class GateRegistry {
  private guides = new Map<string, GateGuideEntry>();
  private logger: Logger;
  private config: GateRegistryConfig;
  private initialized = false;
  private loader: GateDefinitionLoader;

  constructor(logger: Logger, config: Partial<GateRegistryConfig> = {}) {
    this.logger = logger;
    this.config = {
      autoLoadBuiltIn: config.autoLoadBuiltIn ?? true,
      customGuides: config.customGuides ?? [],
      validateOnRegistration: config.validateOnRegistration ?? true,
      ...(config.loaderConfig !== undefined ? { loaderConfig: config.loaderConfig } : {}),
    };

    // Create the definition loader
    this.loader =
      this.config.loaderConfig !== undefined
        ? new GateDefinitionLoader(this.config.loaderConfig)
        : new GateDefinitionLoader();
  }

  /**
   * Initialize the gate registry
   */
  async initialize(): Promise<void> {
    if (this.initialized) {
      this.logger.debug('GateRegistry already initialized');
      return;
    }

    this.logger.info('Initializing GateRegistry...');
    const startTime = performance.now();

    try {
      // Load built-in gate guides if enabled
      if (this.config.autoLoadBuiltIn) {
        await this.loadBuiltInGuides();
      }

      // Load custom guides if provided
      if (this.config.customGuides && this.config.customGuides.length > 0) {
        await this.loadCustomGuides(this.config.customGuides);
      }

      const loadTime = performance.now() - startTime;
      this.initialized = true;

      this.logger.info(
        `GateRegistry initialized with ${this.guides.size} guides in ${loadTime.toFixed(1)}ms`
      );
    } catch (error) {
      this.logger.error('Failed to initialize GateRegistry:', error);
      throw error;
    }
  }

  /**
   * Register a gate guide
   *
   * @param guide - The gate guide to register
   * @param isBuiltIn - Whether this is a built-in gate
   * @param source - How this guide was loaded
   * @returns true if registration succeeded
   */
  async registerGuide(
    guide: GateGuide,
    isBuiltIn: boolean = false,
    source: GateSource = 'custom'
  ): Promise<boolean> {
    const startTime = performance.now();

    try {
      // Validate guide if required
      if (this.config.validateOnRegistration) {
        const validationResult = this.validateGuide(guide);
        if (!validationResult.valid) {
          this.logger.warn(
            `Guide validation failed for ${guide.gateId}: ${validationResult.errors.join(', ')}`
          );
          return false;
        }
      }

      // Check for existing guide with same ID
      const normalizedId = guide.gateId.toLowerCase();
      if (this.guides.has(normalizedId)) {
        this.logger.warn(`Guide with ID '${guide.gateId}' already registered, replacing...`);
      }

      // Create registry entry
      const entry: GateGuideEntry = {
        guide,
        registeredAt: new Date(),
        isBuiltIn,
        enabled: true,
        source,
        metadata: {
          loadTime: performance.now() - startTime,
          validationStatus: this.config.validateOnRegistration ? 'passed' : 'not_validated',
        },
      };

      this.guides.set(normalizedId, entry);

      this.logger.debug(`Registered gate guide: ${guide.name} (${guide.gateId}) [${source}]`);
      return true;
    } catch (error) {
      this.logger.error(`Failed to register guide ${guide.gateId}:`, error);
      return false;
    }
  }

  /**
   * Get a gate guide by ID
   *
   * @param gateId - The gate ID (case-insensitive)
   * @returns The guide or undefined if not found/disabled
   */
  getGuide(gateId: string): GateGuide | undefined {
    const normalizedId = gateId.toLowerCase();
    const entry = this.guides.get(normalizedId);

    if (!entry) {
      return undefined;
    }

    if (!entry.enabled) {
      this.logger.debug(`Gate guide '${gateId}' is disabled`);
      return undefined;
    }

    // Update last used timestamp
    entry.metadata.lastUsed = new Date();

    return entry.guide;
  }

  /**
   * Get all registered guides
   *
   * @param enabledOnly - If true, only return enabled guides
   * @returns Array of gate guides
   */
  getAllGuides(enabledOnly: boolean = true): GateGuide[] {
    const guides: GateGuide[] = [];

    for (const entry of this.guides.values()) {
      if (!enabledOnly || entry.enabled) {
        guides.push(entry.guide);
      }
    }

    return guides;
  }

  /**
   * Get guide entries with metadata
   *
   * @param enabledOnly - If true, only return enabled guides
   * @returns Array of guide entries
   */
  getGuideEntries(enabledOnly: boolean = true): GateGuideEntry[] {
    const entries: GateGuideEntry[] = [];

    for (const entry of this.guides.values()) {
      if (!enabledOnly || entry.enabled) {
        entries.push(entry);
      }
    }

    return entries;
  }

  /**
   * Check if a guide exists
   *
   * @param gateId - The gate ID to check
   * @returns true if the guide exists (regardless of enabled state)
   */
  hasGuide(gateId: string): boolean {
    return this.guides.has(gateId.toLowerCase());
  }

  /**
   * Enable or disable a guide
   *
   * @param gateId - The gate ID
   * @param enabled - Whether to enable or disable
   * @returns true if the operation succeeded
   */
  setGuideEnabled(gateId: string, enabled: boolean): boolean {
    const normalizedId = gateId.toLowerCase();
    const entry = this.guides.get(normalizedId);

    if (!entry) {
      this.logger.warn(`Cannot ${enabled ? 'enable' : 'disable'} unknown gate: ${gateId}`);
      return false;
    }

    entry.enabled = enabled;
    this.logger.info(`Gate '${gateId}' ${enabled ? 'enabled' : 'disabled'}`);
    return true;
  }

  /**
   * Unregister a guide from the registry
   *
   * @param gateId - The gate ID to unregister
   * @returns true if the guide was found and removed
   */
  unregisterGuide(gateId: string): boolean {
    const normalizedId = gateId.toLowerCase();

    if (!this.guides.has(normalizedId)) {
      this.logger.warn(`Cannot unregister unknown gate: ${gateId}`);
      return false;
    }

    this.guides.delete(normalizedId);
    this.logger.info(`Gate '${gateId}' unregistered from registry`);
    return true;
  }

  /**
   * Get registry statistics
   */
  getRegistryStats(): GateRegistryStats {
    let totalLoadTime = 0;
    const bySource: Record<GateSource, number> = {
      'yaml-runtime': 0,
      custom: 0,
      temporary: 0,
    };
    const byType: Record<'validation' | 'guidance', number> = {
      validation: 0,
      guidance: 0,
    };

    let enabledCount = 0;
    let builtInCount = 0;
    let customCount = 0;

    for (const entry of this.guides.values()) {
      totalLoadTime += entry.metadata.loadTime;
      bySource[entry.source]++;
      byType[entry.guide.type]++;

      if (entry.enabled) enabledCount++;
      if (entry.isBuiltIn) builtInCount++;
      else customCount++;
    }

    return {
      totalGates: this.guides.size,
      enabledGates: enabledCount,
      builtInGates: builtInCount,
      customGates: customCount,
      bySource,
      byType,
      averageLoadTime: this.guides.size > 0 ? totalLoadTime / this.guides.size : 0,
    };
  }

  /**
   * Get the underlying definition loader
   */
  getLoader(): GateDefinitionLoader {
    return this.loader;
  }

  /**
   * Reload a specific gate
   *
   * @param gateId - The gate ID to reload
   * @returns true if reload succeeded
   */
  async reloadGuide(gateId: string): Promise<boolean> {
    const normalizedId = gateId.toLowerCase();
    const existingEntry = this.guides.get(normalizedId);

    // Clear loader cache for this gate
    this.loader.clearCache(normalizedId);

    // Load fresh definition
    const definition = this.loader.loadGate(normalizedId) as GateDefinitionYaml;
    if (!definition) {
      this.logger.warn(`Failed to reload gate '${gateId}': definition not found`);
      return false;
    }

    // Create new guide
    const guide = createGenericGateGuide(definition);

    // Re-register with existing metadata where appropriate
    const entry: GateGuideEntry = {
      guide,
      registeredAt: new Date(),
      isBuiltIn: existingEntry?.isBuiltIn ?? false,
      enabled: existingEntry?.enabled ?? true,
      source: 'yaml-runtime',
      metadata: {
        loadTime: 0, // Will be updated
        validationStatus: 'passed',
        ...(existingEntry?.metadata.lastUsed ? { lastUsed: existingEntry.metadata.lastUsed } : {}),
      },
    };

    this.guides.set(normalizedId, entry);
    this.logger.info(`Reloaded gate guide: ${guide.name} (${gateId})`);
    return true;
  }

  // ============================================================================
  // Private Implementation
  // ============================================================================

  /**
   * Load built-in gates from YAML files
   */
  private async loadBuiltInGuides(): Promise<void> {
    const gateIds = this.loader.discoverGates();

    this.logger.debug(`Discovered ${gateIds.length} gates to load`);

    for (const gateId of gateIds) {
      const startTime = performance.now();
      const definition = this.loader.loadGate(gateId) as GateDefinitionYaml | undefined;

      if (!definition) {
        this.logger.warn(`Failed to load gate definition: ${gateId}`);
        continue;
      }

      try {
        const guide = createGenericGateGuide(definition);
        await this.registerGuide(guide, true, 'yaml-runtime');

        // Update load time in entry
        const entry = this.guides.get(gateId.toLowerCase());
        if (entry) {
          entry.metadata.loadTime = performance.now() - startTime;
        }
      } catch (error) {
        this.logger.error(`Failed to create guide for gate '${gateId}':`, error);
      }
    }
  }

  /**
   * Load custom guides provided in config
   */
  private async loadCustomGuides(guides: GateGuide[]): Promise<void> {
    for (const guide of guides) {
      await this.registerGuide(guide, false, 'custom');
    }
  }

  /**
   * Validate a gate guide
   */
  private validateGuide(guide: GateGuide): { valid: boolean; errors: string[] } {
    const errors: string[] = [];

    // Check required properties
    if (!guide.gateId || guide.gateId.trim() === '') {
      errors.push('Gate ID is required');
    }
    if (!guide.name || guide.name.trim() === '') {
      errors.push('Gate name is required');
    }
    if (!guide.type || !['validation', 'guidance'].includes(guide.type)) {
      errors.push('Gate type must be "validation" or "guidance"');
    }

    // Check required methods
    if (typeof guide.getGuidance !== 'function') {
      errors.push('getGuidance() method is required');
    }
    if (typeof guide.isActive !== 'function') {
      errors.push('isActive() method is required');
    }
    // Note: validate() method was removed from GateGuide - use GateValidator instead

    return { valid: errors.length === 0, errors };
  }
}

/**
 * Create a gate registry with default configuration
 */
export async function createGateRegistry(
  logger: Logger,
  config?: Partial<GateRegistryConfig>
): Promise<GateRegistry> {
  const registry = new GateRegistry(logger, config);
  await registry.initialize();
  return registry;
}
