/**
 * Base class for operation providers
 */

import type { Logger, MongoDBClient } from '@stock-bot/types';
import type { 
  OperationConfig, 
  OperationUpdate, 
  ProviderConfig,
  OperationComponentOptions 
} from './types';

/**
 * Abstract base class that all operation providers must extend
 */
export abstract class BaseOperationProvider {
  protected mongodb: MongoDBClient;
  protected logger: Logger;
  private _initialized = false;

  constructor({ mongodb, logger }: OperationComponentOptions) {
    this.mongodb = mongodb;
    this.logger = logger;
  }

  /**
   * Get provider configuration
   */
  abstract getProviderConfig(): ProviderConfig;

  /**
   * Get all operations supported by this provider
   */
  abstract getOperations(): OperationConfig[];

  /**
   * Initialize the provider (optional override)
   */
  async initialize(): Promise<void> {
    if (this._initialized) {
      return;
    }

    this.logger.info(`Initializing ${this.getProviderConfig().name} operation provider`);
    
    // Allow providers to perform custom initialization
    await this.onInitialize();
    
    this._initialized = true;
  }

  /**
   * Check if provider is initialized
   */
  isInitialized(): boolean {
    return this._initialized;
  }

  /**
   * Get operation by name
   */
  getOperation(name: string): OperationConfig | undefined {
    return this.getOperations().find(op => op.name === name);
  }

  /**
   * Validate operation exists
   */
  validateOperation(operationName: string): void {
    const operation = this.getOperation(operationName);
    if (!operation) {
      throw new Error(
        `Unknown operation '${operationName}' for provider '${this.getProviderConfig().name}'`
      );
    }
  }

  /**
   * Get the symbol identifier value from a document
   */
  getSymbolIdentifier(doc: any): string {
    const { symbolField } = this.getProviderConfig();
    const value = doc[symbolField];
    
    if (!value) {
      throw new Error(
        `Symbol field '${symbolField}' not found in document for provider '${this.getProviderConfig().name}'`
      );
    }
    
    return value;
  }

  /**
   * Build operation field path for MongoDB
   */
  getOperationFieldPath(operationName: string, field?: string): string {
    const basePath = `operations.${operationName}`;
    return field ? `${basePath}.${field}` : basePath;
  }

  /**
   * Hook called during initialization (optional override)
   */
  protected async onInitialize(): Promise<void> {
    // Providers can override this for custom initialization
  }

  /**
   * Hook called before operation update (optional override)
   */
  async beforeOperationUpdate(
    symbol: string, 
    operation: string, 
    data: OperationUpdate
  ): Promise<void> {
    // Providers can override this for custom logic
  }

  /**
   * Hook called after operation update (optional override)
   */
  async afterOperationUpdate(
    symbol: string, 
    operation: string, 
    data: OperationUpdate
  ): Promise<void> {
    // Providers can override this for custom logic
  }

  /**
   * Get default stale hours for an operation
   */
  getDefaultStaleHours(operationName: string): number {
    const operation = this.getOperation(operationName);
    return operation?.defaultStaleHours || 24; // Default to 24 hours
  }

  /**
   * Check if operation requires finished flag
   */
  requiresFinishedFlag(operationName: string): boolean {
    const operation = this.getOperation(operationName);
    return operation?.requiresFinishedFlag || false;
  }

  /**
   * Get all operation names
   */
  getOperationNames(): string[] {
    return this.getOperations().map(op => op.name);
  }

  /**
   * Log provider info
   */
  logInfo(): void {
    const config = this.getProviderConfig();
    const operations = this.getOperations();
    
    this.logger.info(`Provider: ${config.name}`, {
      provider: config.name,
      collection: config.collectionName,
      symbolField: config.symbolField,
      operationCount: operations.length,
      operations: operations.map(op => ({
        name: op.name,
        type: op.type,
        defaultStaleHours: op.defaultStaleHours
      }))
    });
  }
}