// @lifecycle canonical - Coordinates methodology selection and framework execution contexts.
/**
 * Framework Manager
 *
 * Orchestration layer for the framework system.
 * Extends BaseResourceHandler to provide unified resource management patterns.
 *
 * Coordinates between:
 * - MethodologyRegistry: Manages methodology guides (source of truth)
 * - FrameworkDefinitions: Generated from methodology guides
 * - FrameworkStateStore: Runtime enable/disable state
 */

import { MethodologyRegistry, createMethodologyRegistry } from './methodology/index.js';
import {
  FrameworkDefinition,
  FrameworkExecutionContext,
  FrameworkMethodology,
  FrameworkSelectionCriteria,
  MethodologyGuide,
} from './types/index.js';
import { Logger } from '../../infra/logging/index.js';
import { BaseResourceHandler } from '../../shared/core/resource-manager/index.js';

import type { ConvertedPrompt } from '../execution/types.js';

/**
 * Framework switch request (matches FrameworkStateStore interface)
 */
interface FrameworkSwitchRequest {
  targetFramework: string;
  reason?: string;
}

/**
 * Framework state accessor interface
 */
interface FrameworkStateAccessor {
  isFrameworkSystemEnabled(): boolean;
  getActiveFramework(): { id: string; type: string; methodology: string } | null | undefined;
  switchFramework(request: FrameworkSwitchRequest): Promise<boolean>;
}

/**
 * Configuration for FrameworkManager
 */
export interface FrameworkManagerConfig {
  /** Default framework to use when none specified */
  defaultFramework?: string;
  /** Enable debug logging */
  debug?: boolean;
}

/**
 * Statistics for FrameworkManager
 */
export interface FrameworkManagerStats {
  /** Total number of registered frameworks */
  totalFrameworks: number;
  /** Number of enabled frameworks */
  enabledFrameworks: number;
  /** Total methodology guides loaded */
  totalMethodologies: number;
  /** Currently active framework */
  activeFramework: string | null;
}

/**
 * Registry entry for frameworks (minimal wrapper since we use direct Map)
 */
export interface FrameworkEntry {
  framework: FrameworkDefinition;
  enabled: boolean;
  registeredAt: Date;
  source: 'methodology' | 'custom';
}

/**
 * Framework Manager
 *
 * Provides methodology selection and system prompt generation.
 * Generates FrameworkDefinitions from MethodologyGuides.
 *
 * @example
 * ```typescript
 * const manager = await createFrameworkManager(logger);
 *
 * // Select framework based on criteria
 * const framework = manager.selectFramework({ userPreference: 'CAGEERF' });
 *
 * // Generate execution context
 * const context = manager.generateExecutionContext(prompt);
 * ```
 */
export class FrameworkManager extends BaseResourceHandler<
  FrameworkDefinition,
  FrameworkEntry,
  FrameworkManagerConfig,
  FrameworkManagerStats
> {
  private frameworks: Map<string, FrameworkDefinition> = new Map();
  private methodologyRegistry: MethodologyRegistry | null = null;
  private defaultFramework: string = 'CAGEERF';
  private frameworkStateStore?: FrameworkStateAccessor;

  constructor(logger: Logger, config: FrameworkManagerConfig = {}) {
    super(logger, config);
    if (config.defaultFramework) {
      this.defaultFramework = config.defaultFramework;
    }
  }

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

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

  protected async initializeRegistry(): Promise<void> {
    // Initialize methodology registry
    this.methodologyRegistry = await createMethodologyRegistry(this.logger);
    this.logger.debug('MethodologyRegistry initialized');
  }

  protected override async postRegistryInit(): Promise<void> {
    // Generate framework definitions from methodology guides
    await this.generateFrameworkDefinitions();
    this.logger.info(`Generated ${this.frameworks.size} framework definitions`);
  }

  protected applyDefaultConfig(config: FrameworkManagerConfig): FrameworkManagerConfig {
    return {
      defaultFramework: config.defaultFramework ?? 'CAGEERF',
      debug: config.debug ?? false,
    };
  }

  protected getResource(id: string): FrameworkDefinition | undefined {
    // Normalize to lowercase - all keys are stored lowercase
    const framework = this.frameworks.get(id.toLowerCase());
    return framework?.enabled ? framework : undefined;
  }

  protected hasResource(id: string): boolean {
    // All keys are stored lowercase
    return this.frameworks.has(id.toLowerCase());
  }

  protected listResources(enabledOnly: boolean): FrameworkDefinition[] {
    const frameworks = Array.from(this.frameworks.values());
    return enabledOnly ? frameworks.filter((f) => f.enabled) : frameworks;
  }

  protected getResourceEntries(enabledOnly: boolean): FrameworkEntry[] {
    const entries: FrameworkEntry[] = [];
    for (const [, framework] of this.frameworks) {
      if (!enabledOnly || framework.enabled) {
        entries.push({
          framework,
          enabled: framework.enabled,
          registeredAt: new Date(),
          source: 'methodology',
        });
      }
    }
    return entries;
  }

  protected setResourceEnabled(id: string, enabled: boolean): boolean {
    const framework = this.getFrameworkById(id);
    if (framework) {
      framework.enabled = enabled;
      this.logger.info(`Framework ${id} ${enabled ? 'enabled' : 'disabled'}`);
      return true;
    }
    return false;
  }

  protected async reloadResource(id: string): Promise<boolean> {
    // For frameworks, reloading means regenerating from methodology guide
    const guide = this.methodologyRegistry?.getGuide(id.toLowerCase());
    if (!guide) return false;

    const definition = this.generateSingleFrameworkDefinition(guide);
    if (definition) {
      // Store with lowercase key for consistent lookup
      this.frameworks.set(definition.id.toLowerCase(), definition);
      this.logger.debug(`Reloaded framework: ${definition.id}`);
      return true;
    }
    return false;
  }

  protected unregisterResource(id: string): boolean {
    const lowerId = id.toLowerCase();
    let removed = false;

    // All keys are stored lowercase
    if (this.frameworks.has(lowerId)) {
      this.frameworks.delete(lowerId);
      removed = true;
    }

    if (this.methodologyRegistry) {
      const guideRemoved = this.methodologyRegistry.unregisterGuide(lowerId);
      if (guideRemoved) removed = true;
    }

    if (removed) {
      this.logger.info(`Framework '${id}' unregistered`);
    }
    return removed;
  }

  protected clearResourceCache(_id?: string): void {
    // MethodologyRegistry manages its own cache internally
    // Cache is cleared automatically on loadAndRegisterById()
  }

  protected getResourceStats(): FrameworkManagerStats {
    const frameworks = Array.from(this.frameworks.values());
    const enabled = frameworks.filter((f) => f.enabled);

    let activeFramework: string | null = null;
    if (this.frameworkStateStore?.isFrameworkSystemEnabled()) {
      const active = this.frameworkStateStore.getActiveFramework();
      if (active) activeFramework = active.type;
    }

    return {
      totalFrameworks: frameworks.length,
      enabledFrameworks: enabled.length,
      totalMethodologies: this.methodologyRegistry?.getAllGuides(false).length ?? 0,
      activeFramework,
    };
  }

  protected override isSystemEnabled(): boolean {
    if (!this.frameworkStateStore) return true;
    return this.frameworkStateStore.isFrameworkSystemEnabled();
  }

  // ============================================================================
  // Domain-Specific Methods
  // ============================================================================

  /**
   * Set the framework state manager for synchronization
   */
  setFrameworkStateStore(frameworkStateStore: FrameworkStateAccessor): void {
    this.frameworkStateStore = frameworkStateStore;
    this.logger.debug('Framework State Manager synchronized with Framework Manager');
  }

  /**
   * Switch to a new framework. Single authority for framework switching.
   * Handles normalization, validation, and delegates persistence to FrameworkStateStore.
   *
   * @param frameworkId - Framework identifier (case-insensitive)
   * @param reason - Optional reason for the switch
   * @returns Result object with success status, framework definition, or error message
   */
  async switchFramework(
    frameworkId: string,
    reason?: string
  ): Promise<{ success: boolean; framework?: FrameworkDefinition; error?: string }> {
    this.ensureInitialized();

    // 1. Normalize ID once at the boundary
    const normalizedId = frameworkId.toLowerCase();

    // 2. Validate framework exists and is enabled
    const framework = this.getFramework(normalizedId);
    if (!framework) {
      const available = this.listFrameworks(true)
        .map((f) => f.id)
        .join(', ');
      return {
        success: false,
        error: `Framework '${frameworkId}' not found. Available: ${available}`,
      };
    }

    if (!framework.enabled) {
      return { success: false, error: `Framework '${frameworkId}' is disabled` };
    }

    // 3. Delegate persistence to state manager
    if (!this.frameworkStateStore) {
      return { success: false, error: 'Framework state manager not initialized' };
    }

    try {
      const success = await this.frameworkStateStore.switchFramework({
        targetFramework: normalizedId,
        reason: reason || `Switched to ${framework.name}`,
      });

      if (success) {
        this.logger.info(`Framework switched to '${framework.name}' (${framework.id})`);
        return { success: true, framework };
      }

      return { success: false, error: 'Framework switch failed in state manager' };
    } catch (error) {
      const errorMsg = error instanceof Error ? error.message : String(error);
      this.logger.error(`Framework switch failed: ${errorMsg}`);
      return { success: false, error: errorMsg };
    }
  }

  /**
   * Select appropriate framework based on criteria
   */
  selectFramework(criteria: FrameworkSelectionCriteria = {}): FrameworkDefinition {
    this.ensureInitialized();

    // User preference takes priority
    if (criteria.userPreference && criteria.userPreference !== 'AUTO') {
      const preferred = this.getFramework(criteria.userPreference);
      if (preferred?.enabled) {
        this.logger.debug(`Framework selected by user preference: ${preferred.name}`);
        return preferred;
      } else {
        this.logger.warn(
          `Requested framework ${criteria.userPreference} not found or disabled, using default`
        );
      }
    }

    // Check state manager for active framework
    if (this.frameworkStateStore?.isFrameworkSystemEnabled()) {
      const activeFramework = this.frameworkStateStore.getActiveFramework();
      if (activeFramework) {
        const framework = this.getFramework(activeFramework.type);
        if (framework?.enabled) {
          this.logger.debug(`Framework selected: ${framework.name} (from active state manager)`);
          return framework;
        }
      }
    }

    // Fallback to default framework
    const defaultFw = this.getFramework(this.defaultFramework);
    if (!defaultFw) {
      throw new Error(`Default framework ${this.defaultFramework} not found`);
    }

    this.logger.debug(`Framework selected: ${defaultFw.name} (default fallback)`);
    return defaultFw;
  }

  /**
   * Generate execution context with system prompts and guidelines
   */
  generateExecutionContext(
    prompt: ConvertedPrompt,
    criteria: FrameworkSelectionCriteria = {}
  ): FrameworkExecutionContext {
    const selectedFramework = this.selectFramework(criteria);
    const systemPrompt = this.generateSystemPrompt(selectedFramework, prompt);

    return {
      selectedFramework,
      systemPrompt,
      executionGuidelines: [...selectedFramework.executionGuidelines],
      metadata: {
        selectionReason: this.getSelectionReason(selectedFramework, criteria),
        confidence: 1.0,
        appliedAt: new Date(),
      },
    };
  }

  /**
   * Get framework by methodology type (case-insensitive)
   */
  getFramework(methodology: string): FrameworkDefinition | undefined {
    return this.get(methodology);
  }

  /**
   * List available frameworks
   */
  listFrameworks(enabledOnly: boolean = false): FrameworkDefinition[] {
    return this.list(enabledOnly);
  }

  /**
   * Check if a framework exists and is enabled
   *
   * @param id - Framework identifier (case-insensitive)
   * @returns true if framework exists and is enabled
   */
  isFrameworkEnabled(id: string): boolean {
    const framework = this.getFramework(id);
    return framework?.enabled ?? false;
  }

  /**
   * Get list of framework IDs
   *
   * @param enabledOnly - Only return enabled frameworks (default: false)
   * @returns Array of framework IDs in uppercase
   */
  getFrameworkIds(enabledOnly: boolean = false): string[] {
    return this.listFrameworks(enabledOnly).map((f) => f.id);
  }

  /**
   * Validate framework identifier and return normalized ID or error details
   *
   * @param id - Framework identifier to validate
   * @returns Validation result with normalized ID or error message
   */
  validateIdentifier(id: string): {
    valid: boolean;
    normalizedId?: string;
    error?: string;
    suggestions?: string[];
  } {
    if (id === '' || typeof id !== 'string') {
      return {
        valid: false,
        error: 'Framework identifier is required',
        suggestions: this.getFrameworkIds(false),
      };
    }

    const trimmed = id.trim();
    if (trimmed === '') {
      return {
        valid: false,
        error: 'Framework identifier cannot be empty',
        suggestions: this.getFrameworkIds(false),
      };
    }

    const normalizedId = trimmed.toUpperCase();
    const framework = this.getFramework(normalizedId);

    if (framework !== undefined) {
      return {
        valid: true,
        normalizedId: framework.id,
      };
    }

    return {
      valid: false,
      error: `Framework '${id}' not found`,
      suggestions: this.getFrameworkIds(false),
    };
  }

  /**
   * Get methodology guide by framework ID
   */
  getMethodologyGuide(frameworkId: string): MethodologyGuide | undefined {
    this.ensureInitialized();
    return this.methodologyRegistry!.getGuide(frameworkId.toLowerCase());
  }

  /**
   * List available methodology guides
   */
  listMethodologyGuides(): MethodologyGuide[] {
    this.ensureInitialized();
    return this.methodologyRegistry!.getAllGuides(true);
  }

  /**
   * Expose the methodology registry for integrations
   */
  getMethodologyRegistry(): MethodologyRegistry {
    this.ensureInitialized();
    if (!this.methodologyRegistry) {
      throw new Error('Methodology registry not initialized');
    }
    return this.methodologyRegistry;
  }

  /**
   * Set default framework
   */
  setDefaultFramework(methodology: FrameworkMethodology): void {
    if (this.hasResource(methodology)) {
      this.defaultFramework = methodology;
      this.logger.info(`Default framework set to: ${methodology}`);
    } else {
      throw new Error(`Framework ${methodology} not found`);
    }
  }

  /**
   * Register a new framework by loading from disk
   */
  async registerFramework(frameworkId: string): Promise<boolean> {
    this.ensureInitialized();

    const normalizedId = frameworkId.toLowerCase();

    try {
      if (!this.methodologyRegistry) {
        this.logger.error('MethodologyRegistry not available');
        return false;
      }

      const guideLoaded = await this.methodologyRegistry.loadAndRegisterById(normalizedId);
      if (!guideLoaded) {
        this.logger.warn(`Failed to load methodology guide for '${frameworkId}'`);
        return false;
      }

      const guide = this.methodologyRegistry.getGuide(normalizedId);
      if (!guide) {
        this.logger.error(`Guide loaded but cannot be retrieved: ${frameworkId}`);
        return false;
      }

      const definition = this.generateSingleFrameworkDefinition(guide);
      if (definition) {
        // Store with lowercase key for consistent lookup
        this.frameworks.set(normalizedId, definition);
        this.logger.info(`Framework '${frameworkId}' registered`);
        return true;
      }

      return false;
    } catch (error) {
      this.logger.error(`Failed to register framework '${frameworkId}':`, error);
      return false;
    }
  }

  // ============================================================================
  // Private Helper Methods
  // ============================================================================

  /**
   * Get framework by ID (all keys are stored lowercase)
   */
  private getFrameworkById(id: string): FrameworkDefinition | undefined {
    return this.frameworks.get(id.toLowerCase());
  }

  /**
   * Generate framework definitions from methodology guides
   */
  private async generateFrameworkDefinitions(): Promise<void> {
    try {
      const guides = this.methodologyRegistry!.getAllGuides(true);

      for (const guide of guides) {
        const definition = this.generateSingleFrameworkDefinition(guide);
        if (definition) {
          // Store with lowercase key for consistent lookup
          this.frameworks.set(definition.id.toLowerCase(), definition);
          this.logger.debug(`Generated framework definition for ${guide.frameworkName}`);
        }
      }

      this.logger.info(
        `Generated ${this.frameworks.size} framework definitions from methodology guides`
      );
    } catch (error) {
      this.logger.error('Failed to generate framework definitions:', error);
      throw error;
    }
  }

  /**
   * Generate a single framework definition from a methodology guide
   */
  private generateSingleFrameworkDefinition(guide: MethodologyGuide): FrameworkDefinition | null {
    try {
      const systemPromptTemplate = this.generateSystemPromptTemplate(guide);

      return {
        id: guide.frameworkId.toUpperCase(),
        name: guide.frameworkName,
        description: this.getFrameworkDescription(guide),
        type: guide.type,
        methodology: guide.type, // Backward compat: methodology mirrors type
        systemPromptTemplate,
        executionGuidelines: this.getExecutionGuidelines(guide),
        applicableTypes: this.getApplicableTypes(guide),
        priority: this.getFrameworkPriority(guide),
        enabled: true,
      };
    } catch (error) {
      this.logger.error(`Failed to generate definition for ${guide.frameworkId}:`, error);
      return null;
    }
  }

  /**
   * Generate system prompt template wrapper
   */
  private generateSystemPromptTemplate(guide: MethodologyGuide): string {
    return `You are operating under the ${guide.frameworkName} methodology for {PROMPT_NAME}.

{METHODOLOGY_GUIDANCE}

Apply this methodology systematically to ensure comprehensive and structured responses.`;
  }

  /**
   * Get framework description
   */
  private getFrameworkDescription(guide: MethodologyGuide): string {
    switch (guide.type) {
      case 'CAGEERF':
        return 'Comprehensive structured approach: Context, Analysis, Goals, Execution, Evaluation, Refinement, Framework';
      case 'ReACT':
        return 'Reasoning and Acting pattern for systematic problem-solving';
      case '5W1H':
        return 'Who, What, When, Where, Why, How systematic analysis';
      case 'SCAMPER':
        return 'Creative problem-solving: Substitute, Combine, Adapt, Modify, Put to other uses, Eliminate, Reverse';
      default:
        return `${guide.type} methodology for systematic approach`;
    }
  }

  /**
   * Get execution guidelines from methodology guide
   */
  private getExecutionGuidelines(guide: MethodologyGuide): string[] {
    const processingGuidance = guide.guideTemplateProcessing('', 'single');
    return processingGuidance.templateEnhancements.systemPromptAdditions;
  }

  /**
   * Get applicable types for framework
   */
  private getApplicableTypes(guide: MethodologyGuide): string[] {
    switch (guide.type) {
      case 'CAGEERF':
        return ['chain', 'template'];
      case 'ReACT':
        return ['chain'];
      case '5W1H':
        return ['template', 'chain'];
      case 'SCAMPER':
        return ['template'];
      default:
        return ['template'];
    }
  }

  /**
   * Get framework priority
   */
  private getFrameworkPriority(guide: MethodologyGuide): number {
    switch (guide.type) {
      case 'CAGEERF':
        return 10;
      case 'ReACT':
        return 8;
      case '5W1H':
        return 7;
      case 'SCAMPER':
        return 6;
      default:
        return 5;
    }
  }

  /**
   * Generate framework-specific system prompt
   */
  private generateSystemPrompt(framework: FrameworkDefinition, prompt: ConvertedPrompt): string {
    let systemPrompt = framework.systemPromptTemplate;

    systemPrompt = systemPrompt.replace(/\{PROMPT_NAME\}/g, prompt.name || 'Prompt');
    systemPrompt = systemPrompt.replace(/\{PROMPT_CATEGORY\}/g, prompt.category || 'general');
    systemPrompt = systemPrompt.replace(/\{FRAMEWORK_NAME\}/g, framework.name);

    const guide = this.getMethodologyGuide(framework.id);
    if (guide) {
      const guidance = guide.getSystemPromptGuidance({
        promptName: prompt.name,
        promptCategory: prompt.category,
      });
      systemPrompt = systemPrompt.replace(/\{METHODOLOGY_GUIDANCE\}/g, guidance);
    }

    return systemPrompt;
  }

  /**
   * Get selection reason for context metadata
   */
  private getSelectionReason(
    framework: FrameworkDefinition,
    criteria: FrameworkSelectionCriteria
  ): string {
    if (criteria.userPreference && criteria.userPreference !== 'AUTO') {
      return `User preference: ${criteria.userPreference}`;
    }
    return 'Default framework selection';
  }
}

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

// Export types
export type { FrameworkDefinition, FrameworkExecutionContext, FrameworkSelectionCriteria };
