// @lifecycle canonical - Coordinates methodology hot reload between file watcher and registry
/**
 * Methodology Hot Reload Coordinator
 *
 * Handles the integration between file system watching and methodology registry,
 * enabling hot reload of methodology definitions when YAML files change.
 */

import { createGenericGuide } from './generic-methodology-guide.js';
import { RuntimeMethodologyLoader } from './runtime-methodology-loader.js';

import type { MethodologyRegistry } from './registry.js';
import type { Logger } from '../../../infra/logging/index.js';
import type { HotReloadEvent } from '../../../shared/types/index.js';

/**
 * Configuration for MethodologyHotReloadCoordinator
 */
export interface MethodologyHotReloadConfig {
  /** Enable debug logging */
  debug?: boolean;
  /** Reload timeout in ms */
  reloadTimeoutMs?: number;
  /**
   * Callback invoked when a methodology is deleted.
   * Use this to notify FrameworkManager to clear its frameworks Map.
   */
  onMethodologyDeleted?: (methodologyId: string) => Promise<void> | void;
  /**
   * Callback invoked when a methodology is reloaded (added/modified).
   * Use this to notify FrameworkManager to refresh its framework definition.
   */
  onMethodologyReloaded?: (methodologyId: string) => Promise<void> | void;
}

/**
 * Statistics for hot reload operations
 */
export interface MethodologyHotReloadStats {
  reloadsAttempted: number;
  reloadsSucceeded: number;
  reloadsFailed: number;
  lastReloadTime?: number;
  lastReloadedMethodology?: string;
}

/**
 * Result returned when creating a methodology hot reload registration
 */
export interface MethodologyHotReloadRegistration {
  /** Directories that should be watched for methodology changes */
  directories: string[];
  /** Bound handler for use with HotReloadObserver.setMethodologyReloadCallback */
  handler: (event: HotReloadEvent) => Promise<void>;
  /** Coordinator instance handling cache clear + re-register */
  coordinator: MethodologyHotReloadCoordinator;
}

/**
 * Methodology Hot Reload Coordinator
 *
 * Coordinates between the file watching system and methodology registry to
 * enable seamless hot reload of methodology definitions.
 *
 * @example
 * ```typescript
 * const coordinator = new MethodologyHotReloadCoordinator(logger, registry, loader);
 *
 * // Register with hot reload manager
 * hotReloadObserver.setMethodologyReloadCallback(
 *   (event) => coordinator.handleMethodologyChange(event)
 * );
 * ```
 */
/**
 * Internal config type with required defaults but optional callbacks
 */
interface StoredHotReloadConfig {
  debug: boolean;
  reloadTimeoutMs: number;
  onMethodologyDeleted?: (methodologyId: string) => Promise<void> | void;
  onMethodologyReloaded?: (methodologyId: string) => Promise<void> | void;
}

export class MethodologyHotReloadCoordinator {
  private logger: Logger;
  private registry: MethodologyRegistry;
  private loader: RuntimeMethodologyLoader;
  private config: StoredHotReloadConfig;
  private stats: MethodologyHotReloadStats;

  constructor(
    logger: Logger,
    registry: MethodologyRegistry,
    loader?: RuntimeMethodologyLoader,
    config: MethodologyHotReloadConfig = {}
  ) {
    this.logger = logger;
    this.registry = registry;
    this.loader = loader ?? new RuntimeMethodologyLoader();
    this.config = {
      debug: config.debug ?? false,
      reloadTimeoutMs: config.reloadTimeoutMs ?? 5000,
      onMethodologyDeleted: config.onMethodologyDeleted,
      onMethodologyReloaded: config.onMethodologyReloaded,
    };
    this.stats = {
      reloadsAttempted: 0,
      reloadsSucceeded: 0,
      reloadsFailed: 0,
    };
  }

  /**
   * Handle a methodology file change event
   *
   * For 'removed' events: unregisters the methodology from the registry
   * For other events: reloads the definition from YAML and re-registers
   *
   * @param event - Hot reload event from the file watcher
   */
  async handleMethodologyChange(event: HotReloadEvent): Promise<void> {
    this.stats.reloadsAttempted++;

    const methodologyId = event.methodologyId;
    if (!methodologyId) {
      this.logger.warn('Methodology hot reload event missing methodologyId, skipping');
      this.stats.reloadsFailed++;
      return;
    }

    if (this.config.debug) {
      this.logger.debug(
        `Processing methodology hot reload for: ${methodologyId} (changeType: ${event.changeType ?? 'unknown'})`
      );
    }

    // Handle deletion events
    if (event.changeType === 'removed') {
      return this.handleMethodologyDeletion(methodologyId);
    }

    // Handle add/modify events
    return this.handleMethodologyReload(methodologyId);
  }

  /**
   * Handle methodology deletion - unregister from registry and notify framework manager
   */
  private async handleMethodologyDeletion(methodologyId: string): Promise<void> {
    try {
      // Step 1: Clear loader cache
      this.loader.clearCache(methodologyId);

      // Step 2: Unregister from registry
      const removed = this.registry.unregisterGuide(methodologyId);

      // Step 3: Notify framework manager to clear its frameworks Map
      if (this.config.onMethodologyDeleted) {
        await this.config.onMethodologyDeleted(methodologyId);
      }

      if (removed) {
        this.stats.reloadsSucceeded++;
        this.stats.lastReloadTime = Date.now();
        this.stats.lastReloadedMethodology = methodologyId;

        this.logger.info(`🗑️ Methodology '${methodologyId}' unregistered (files deleted)`);
      } else {
        this.logger.debug(`Methodology '${methodologyId}' was not registered, nothing to remove`);
        this.stats.reloadsSucceeded++; // Not a failure, just nothing to do
      }
    } catch (error) {
      this.stats.reloadsFailed++;
      this.logger.error(`Failed to unregister methodology '${methodologyId}':`, error);
      throw error;
    }
  }

  /**
   * Handle methodology reload - reload from YAML and re-register
   */
  private async handleMethodologyReload(methodologyId: string): Promise<void> {
    try {
      // Step 1: Clear loader cache for this methodology
      this.loader.clearCache(methodologyId);
      if (this.config.debug) {
        this.logger.debug(`Cleared cache for methodology: ${methodologyId}`);
      }

      // Step 2: Reload definition from YAML
      const definition = this.loader.loadMethodology(methodologyId);
      if (!definition) {
        throw new Error(`Failed to load methodology definition for '${methodologyId}'`);
      }

      if (this.config.debug) {
        this.logger.debug(`Reloaded definition for methodology: ${definition.name}`);
      }

      // Step 3: Create new guide from definition
      const guide = createGenericGuide(definition);

      // Step 4: Re-register with registry (replace existing)
      const success = await this.registry.registerGuide(guide, true, 'yaml-runtime');
      if (!success) {
        throw new Error(`Failed to re-register methodology '${methodologyId}' with registry`);
      }

      // Step 5: Notify framework manager to refresh its framework definition
      if (this.config.onMethodologyReloaded) {
        await this.config.onMethodologyReloaded(methodologyId);
      }

      // Update stats
      this.stats.reloadsSucceeded++;
      this.stats.lastReloadTime = Date.now();
      this.stats.lastReloadedMethodology = methodologyId;

      this.logger.info(
        `🔄 Methodology '${definition.name}' (${methodologyId}) hot reloaded successfully`
      );
    } catch (error) {
      this.stats.reloadsFailed++;
      this.logger.error(`Failed to hot reload methodology '${methodologyId}':`, error);
      throw error;
    }
  }

  /**
   * Get hot reload statistics
   */
  getStats(): MethodologyHotReloadStats {
    return { ...this.stats };
  }

  /**
   * Reset statistics
   */
  resetStats(): void {
    this.stats = {
      reloadsAttempted: 0,
      reloadsSucceeded: 0,
      reloadsFailed: 0,
    };
  }

  /**
   * Get the runtime loader being used
   */
  getLoader(): RuntimeMethodologyLoader {
    return this.loader;
  }
}

/**
 * Create a registration bundle for methodology hot reload.
 * Keeps HotReloadObserver generic by returning only the callback + watch paths.
 */
export function createMethodologyHotReloadRegistration(
  logger: Logger,
  registry: MethodologyRegistry,
  loader?: RuntimeMethodologyLoader,
  config?: MethodologyHotReloadConfig
): MethodologyHotReloadRegistration {
  const runtimeLoader = loader ?? registry.getRuntimeLoader();
  const coordinator = new MethodologyHotReloadCoordinator(logger, registry, runtimeLoader, config);

  return {
    directories: [runtimeLoader.getMethodologiesDir()],
    handler: (event: HotReloadEvent) => coordinator.handleMethodologyChange(event),
    coordinator,
  };
}

/**
 * Factory function to create a MethodologyHotReloadCoordinator
 */
export function createMethodologyHotReloadCoordinator(
  logger: Logger,
  registry: MethodologyRegistry,
  loader?: RuntimeMethodologyLoader,
  config?: MethodologyHotReloadConfig
): MethodologyHotReloadCoordinator {
  return new MethodologyHotReloadCoordinator(logger, registry, loader, config);
}
