// @lifecycle canonical - Renders gate guidance text for prompts/chains.
/**
 * Gate Guidance Renderer - User-Facing Guidance Generation
 *
 * Single responsibility: Generate and format gate guidance for users.
 */

import { filterFrameworkGuidance, hasFrameworkSpecificContent } from './FrameworkGuidanceFilter.js';

import type { Logger } from '../../../infra/logging/index.js';
import type { GateContext } from '../core/gate-definitions.js';
import type { GateDefinitionProvider } from '../core/gate-loader.js';
import type { TemporaryGateRegistry } from '../core/temporary-gate-registry.js';
import type { GateActivationContext, LightweightGateDefinition } from '../types.js';

export interface GateGuidanceRendererOptions {
  gateLoader: GateDefinitionProvider;
  temporaryGateRegistry?: TemporaryGateRegistry;
  frameworkIdentifierProvider?: () => readonly string[] | undefined;
}

/**
 * Gate guidance renderer with framework-specific filtering and temporary gate support
 */
export class GateGuidanceRenderer {
  private readonly logger: Logger;
  private readonly gateLoader: GateDefinitionProvider;
  private readonly temporaryGateRegistry: TemporaryGateRegistry | undefined;
  private readonly frameworkIdentifierProvider: (() => readonly string[] | undefined) | undefined;

  constructor(logger: Logger, options: GateGuidanceRendererOptions) {
    if (!options?.gateLoader) {
      throw new Error('GateGuidanceRenderer requires a gate loader/provider instance');
    }

    this.logger = logger;
    this.gateLoader = options.gateLoader;
    this.temporaryGateRegistry = options.temporaryGateRegistry;
    this.frameworkIdentifierProvider = options.frameworkIdentifierProvider;

    if (this.temporaryGateRegistry) {
      this.logger.debug('[GATE GUIDANCE RENDERER] Temporary gate registry enabled');
    }
    this.logger.debug('[GATE GUIDANCE RENDERER] Initialized with shared gate provider cache');
  }

  /**
   * Generate formatted gate guidance for display to users
   *
   * @param gateIds - Array of gate IDs to render
   * @param context - Context for gate activation and framework filtering
   * @returns Formatted guidance text ready for display
   */
  async renderGuidance(gateIds: string[], context: GateContext = {}): Promise<string> {
    this.logger.info('🎨 [GATE GUIDANCE RENDERER] renderGuidance called:', {
      gateIds,
      framework: context.framework,
      category: context.category,
    });

    if (gateIds.length === 0) {
      this.logger.debug('[GATE GUIDANCE RENDERER] No gates provided, returning empty guidance');
      return '';
    }

    const inlineGuidance: string[] = [];
    const frameworkGuidance: string[] = [];
    const explicitSet = new Set(context.explicitGateIds ?? []);

    for (const gateId of gateIds) {
      try {
        const gate = await this.loadGateDefinition(gateId);
        if (!gate) {
          this.logger.debug('[GATE GUIDANCE RENDERER] Failed to load gate:', gateId);
          continue;
        }

        const isExplicit = explicitSet.has(gateId);
        const inline = this.isInlineGate(gateId, gate) || isExplicit;

        if (!inline && !this.isGateActive(gate, context, isExplicit)) {
          this.logger.debug('[GATE GUIDANCE RENDERER] Skipped gate (not applicable):', gateId);
          continue;
        }

        const formatted = this.formatGateGuidance(gate, context);
        if (inline) {
          inlineGuidance.push(formatted);
        } else {
          frameworkGuidance.push(formatted);
        }
        this.logger.debug('[GATE GUIDANCE RENDERER] Added guidance for gate:', gateId);
      } catch (error) {
        this.logger.warn('[GATE GUIDANCE RENDERER] Failed to load gate:', gateId, error);
      }
    }

    if (inlineGuidance.length === 0 && frameworkGuidance.length === 0) {
      this.logger.debug(
        '[GATE GUIDANCE RENDERER] No applicable gates found, returning empty guidance'
      );
      return '';
    }

    const sections: string[] = ['\n\n---\n\n## Inline Gates'];

    // Add inline guidance first (task-specific)
    if (inlineGuidance.length > 0) {
      sections.push('\n');
      sections.push([...new Set(inlineGuidance)].join('\n\n'));
    }

    // Add framework guidance second (universal standards)
    if (frameworkGuidance.length > 0) {
      sections.push('\n\n');
      sections.push(frameworkGuidance.join('\n\n'));
    }

    // Add post-execution review LAST (final reminder)
    sections.push('\n\n**Post-Execution Review Guidelines:**');
    sections.push(
      'Review your output against these quality standards before finalizing your response.'
    );

    sections.push('\n\n---');

    const supplementalGuidance = sections.join('');

    this.logger.debug('[GATE GUIDANCE RENDERER] Generated supplemental guidance:', {
      inlineGateCount: inlineGuidance.length,
      frameworkGateCount: frameworkGuidance.length,
      guidanceLength: supplementalGuidance.length,
    });

    return supplementalGuidance;
  }

  private isInlineGate(gateId: string, gate: LightweightGateDefinition): boolean {
    // Auto-generated inline gates
    if (gateId.startsWith('inline_gate_')) {
      return true;
    }

    // User-provided temporary gates (should always display, bypass activation checks)
    if (gateId.startsWith('temp_') || this.temporaryGateRegistry?.getTemporaryGate(gateId)) {
      return true;
    }

    // Gates explicitly named as inline quality criteria
    const normalizedName = gate.name?.toLowerCase() ?? '';
    return normalizedName.includes('inline quality') || normalizedName.includes('inline gate');
  }

  /**
   * Load gate definition via the shared gate loader
   *
   * Note: GateLoader already checks the temporary registry, so no fallback needed here.
   * This eliminates duplication and trusts the loader to handle all gate sources.
   */
  private async loadGateDefinition(gateId: string): Promise<LightweightGateDefinition | null> {
    const gate = await this.gateLoader.loadGate(gateId);

    if (!gate) {
      this.logger.warn('[GATE GUIDANCE RENDERER] Gate definition not found:', gateId);
    }

    return gate;
  }

  /**
   * Check if gate should be activated for current context
   *
   * Framework gates (gate_type: "framework") bypass category checks and activate
   * based on framework context alone. This ensures framework methodology guidance
   * applies universally across all categories.
   */
  private isGateActive(
    gate: LightweightGateDefinition,
    context: GateContext,
    explicit: boolean = false
  ): boolean {
    const activationContext: GateActivationContext = { explicitRequest: explicit };
    if (context.category) {
      activationContext.promptCategory = context.category;
    }
    if (context.framework) {
      activationContext.framework = context.framework;
    }
    return this.gateLoader.isGateActive(gate, activationContext);
  }

  /**
   * Format gate guidance for display with framework-specific filtering
   */
  private formatGateGuidance(gate: LightweightGateDefinition, context: GateContext): string {
    let guidance = gate.guidance ?? '';
    const frameworkNames = this.frameworkIdentifierProvider?.();

    if (context.framework && hasFrameworkSpecificContent(guidance, frameworkNames)) {
      guidance = filterFrameworkGuidance(guidance, context.framework, frameworkNames);
      this.logger.debug(
        '[GATE GUIDANCE RENDERER] Applied framework filtering for:',
        context.framework
      );
    }

    // Skip header for auto-generated inline gates - they're already under the section header
    if (gate.name === 'Inline Quality Criteria' || gate.name === 'Inline Validation Criteria') {
      return guidance;
    }

    return `### ${gate.name}\n${guidance}`;
  }

  /**
   * Get available gate IDs (for testing and diagnostics)
   */
  async getAvailableGates(): Promise<string[]> {
    return this.gateLoader.listAvailableGates();
  }

  /**
   * Get detailed gate definitions for listing/discovery
   */
  async getAvailableGateDefinitions(): Promise<LightweightGateDefinition[]> {
    return this.gateLoader.listAvailableGateDefinitions();
  }

  /**
   * Clear cache (for hot-reloading support)
   */
  clearCache(): void {
    this.gateLoader.clearCache();
    this.logger.debug('[GATE GUIDANCE RENDERER] Delegated cache clear to GateLoader');
  }

  /**
   * Get renderer statistics (for monitoring)
   */
  getStatistics(): { cachedGates: number; gatesDirectory: string } {
    const stats = this.gateLoader.getStatistics();
    return {
      cachedGates: stats.cachedGates,
      gatesDirectory: 'loader-managed',
    };
  }
}

/**
 * Factory function for creating gate guidance renderer
 */
export function createGateGuidanceRenderer(
  logger: Logger,
  options: GateGuidanceRendererOptions
): GateGuidanceRenderer {
  return new GateGuidanceRenderer(logger, options);
}
