/**
 * OperationContext - Unified context for handler operations
 */

import { getLogger, type Logger } from '@stock-bot/logger';

interface ServiceResolver {
  resolve<T>(serviceName: string): T;
  resolveAsync<T>(serviceName: string): Promise<T>;
}

export interface OperationContextOptions {
  handlerName: string;
  operationName: string;
  parentLogger?: Logger;
  container?: ServiceResolver;
  metadata?: Record<string, any>;
  traceId?: string;
}

export class OperationContext {
  public readonly logger: Logger;
  public readonly traceId: string;
  public readonly metadata: Record<string, any>;
  private readonly container?: ServiceResolver;
  private readonly startTime: Date;

  constructor(options: OperationContextOptions) {
    this.container = options.container;
    this.metadata = options.metadata || {};
    this.traceId = options.traceId || this.generateTraceId();
    this.startTime = new Date();

    this.logger =
      options.parentLogger ||
      getLogger(`${options.handlerName}:${options.operationName}`, {
        traceId: this.traceId,
        metadata: this.metadata,
      });
  }

  /**
   * Creates a new OperationContext with automatic resource management
   */
  static create(
    handlerName: string,
    operationName: string,
    options: {
      container?: ServiceResolver;
      parentLogger?: Logger;
      metadata?: Record<string, any>;
      traceId?: string;
    } = {}
  ): OperationContext {
    return new OperationContext({
      handlerName,
      operationName,
      ...options,
    });
  }

  /**
   * Resolve a service from the container
   */
  resolve<T>(serviceName: string): T {
    if (!this.container) {
      throw new Error('No service container available');
    }
    return this.container.resolve<T>(serviceName);
  }

  /**
   * Resolve a service asynchronously from the container
   */
  async resolveAsync<T>(serviceName: string): Promise<T> {
    if (!this.container) {
      throw new Error('No service container available');
    }
    return this.container.resolveAsync<T>(serviceName);
  }

  /**
   * Add metadata to the context
   */
  addMetadata(key: string, value: any): void {
    this.metadata[key] = value;
  }

  /**
   * Get execution time in milliseconds
   */
  getExecutionTime(): number {
    return Date.now() - this.startTime.getTime();
  }

  /**
   * Log operation completion with metrics
   */
  logCompletion(success: boolean, error?: Error): void {
    const executionTime = this.getExecutionTime();

    if (success) {
      this.logger.info('Operation completed successfully', {
        executionTime,
        metadata: this.metadata,
      });
    } else {
      this.logger.error('Operation failed', {
        executionTime,
        error: error?.message,
        stack: error?.stack,
        metadata: this.metadata,
      });
    }
  }

  /**
   * Cleanup method
   */
  async dispose(): Promise<void> {
    this.logCompletion(true);
  }

  /**
   * Create child context
   */
  createChild(operationName: string, metadata?: Record<string, any>): OperationContext {
    return new OperationContext({
      handlerName: 'child',
      operationName,
      parentLogger: this.logger,
      container: this.container,
      traceId: this.traceId,
      metadata: { ...this.metadata, ...metadata },
    });
  }

  /**
   * Generate a unique trace ID
   */
  private generateTraceId(): string {
    return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }
}
