import type { IMultiSessionDetailedRunner } from '../interfaces/core/IMultiSessionDetailedRunner';
import type { SessionState, SessionResult, DetailedReport, CombinedReport, MultiSessionConfig } from '../interfaces/core/types';
import type { RunnerEventType, RunnerEvent } from '../interfaces/events/runner-events';
import type { SessionEventType, SessionEvent } from '../interfaces/events/session-events';
import type { BatchExecutionOptions } from '../interfaces/core/batch-execution';

// Implementation imports
import { SessionMetaRepository } from './repositories/session-meta-repository';
import { ReportRepository } from './repositories/report-repository';
import { UnifiedEventBus } from './events/unified-event-bus';
import { MochaIntegration } from './infrastructure/mocha-integration';
import { ReportService } from './services/report-service';

// Use case imports
import { 
  InitializeRunnerUseCase,
  DefineSessionUseCase,
  RunSingleSessionUseCase,
  RunAllSessionsUseCase,
  ClearAllSessionsUseCase,
  DestroyRunnerUseCase
} from './use-cases/runner-use-cases';

export class MultiSessionDetailedRunner implements IMultiSessionDetailedRunner {
  // Dependencies
  private sessionRepository: SessionMetaRepository;
  private reportRepository: ReportRepository;
  private eventBus: UnifiedEventBus;
  private mochaIntegration: MochaIntegration;
  private reportService: ReportService;

  // Use cases
  private initializeUseCase: InitializeRunnerUseCase;
  private defineSessionUseCase: DefineSessionUseCase;
  private runSingleSessionUseCase: RunSingleSessionUseCase;
  private runAllSessionsUseCase: RunAllSessionsUseCase;
  private clearAllSessionsUseCase: ClearAllSessionsUseCase;
  private destroyRunnerUseCase: DestroyRunnerUseCase;

  // State
  private initialized = false;
  private destroyed = false;

  constructor() {
    // Initialize dependencies
    this.sessionRepository = new SessionMetaRepository();
    this.reportRepository = new ReportRepository();
    this.eventBus = new UnifiedEventBus();
    this.mochaIntegration = new MochaIntegration();
    this.reportService = new ReportService(
      this.reportRepository,
      this.sessionRepository,
      this.eventBus
    );

    // Initialize use cases
    this.initializeUseCase = new InitializeRunnerUseCase(
      this.mochaIntegration,
      this.eventBus
    );
    
    this.defineSessionUseCase = new DefineSessionUseCase(
      this.sessionRepository,
      this.reportRepository,
      this.eventBus
    );

    this.runSingleSessionUseCase = new RunSingleSessionUseCase(
      this.sessionRepository,
      this.reportRepository,
      this.eventBus
    );

    this.runAllSessionsUseCase = new RunAllSessionsUseCase(
      this.sessionRepository,
      this.runSingleSessionUseCase,
      this.eventBus
    );

    this.clearAllSessionsUseCase = new ClearAllSessionsUseCase(
      this.sessionRepository,
      this.reportRepository,
      this.eventBus
    );

    this.destroyRunnerUseCase = new DestroyRunnerUseCase(
      this.clearAllSessionsUseCase,
      this.eventBus
    );

    // Bridge session events from the existing library
    this.bridgeExistingSessionEvents();
  }

  // Core Management
  init(config?: Partial<MultiSessionConfig>): void {
    if (this.destroyed) {
      throw new Error('Runner has been destroyed');
    }

    // Set initialized flag before calling use case to avoid timing issues
    this.initialized = true;
    
    try {
      this.initializeUseCase.execute(config);
    } catch (error) {
      // Reset initialized flag on error
      this.initialized = false;
      throw error;
    }
  }

  define(label: string, setupFn: () => void): void {
    this.ensureInitialized();
    this.defineSessionUseCase.execute(label, setupFn);
  }

  async run(label: string): Promise<SessionResult> {
    this.ensureInitialized();
    return this.runSingleSessionUseCase.execute(label);
  }

  async runAll(options?: BatchExecutionOptions): Promise<Record<string, SessionResult>> {
    this.ensureInitialized();
    return this.runAllSessionsUseCase.execute(options);
  }

  // Report Access
  getReport(label: string): DetailedReport | null {
    this.ensureInitialized();
    return this.reportService.getSessionReport(label);
  }

  getSessionStates(): Record<string, SessionState> {
    this.ensureInitialized();
    return this.sessionRepository.getStates();
  }

  getCombinedReport(): CombinedReport {
    this.ensureInitialized();
    return this.reportService.generateCombinedReport();
  }

  // Event System
  onRunnerEvent(eventType: RunnerEventType, callback: (event: RunnerEvent) => void): () => void {
    return this.eventBus.onRunnerEvent(eventType, callback);
  }

  onSessionEvent(eventType: SessionEventType, callback: (event: SessionEvent) => void): () => void {
    return this.eventBus.onSessionEvent(eventType, callback);
  }

  onAnyEvent(callback: (event: RunnerEvent | SessionEvent) => void): () => void {
    return this.eventBus.onAnyEvent(callback);
  }

  // State Queries
  isInitialized(): boolean {
    return this.initialized && !this.destroyed;
  }

  hasSession(label: string): boolean {
    return this.sessionRepository.exists(label);
  }

  getSessionCount(): number {
    return this.sessionRepository.findAll().length;
  }

  // Utility
  async clear(): Promise<void> {
    this.ensureInitialized();
    await this.clearAllSessionsUseCase.execute();
  }

  async destroy(): Promise<void> {
    if (this.destroyed) return;
    
    await this.destroyRunnerUseCase.execute();
    this.destroyed = true;
    this.initialized = false;
  }

  // Private helper methods
  private ensureInitialized(): void {
    if (this.destroyed) {
      throw new Error('Runner has been destroyed');
    }
    if (!this.initialized) {
      throw new Error('Runner not initialized. Call init() first.');
    }
  }

  private bridgeExistingSessionEvents(): void {
    // Bridge events from the existing mocha-multiple-sessions library
    if (typeof window !== 'undefined' && window.MochaMultipleSessions?.onAnySessionEvent) {
      window.MochaMultipleSessions.onAnySessionEvent((event: any) => {
        // Convert and forward the event
        const sessionEvent: SessionEvent = {
          type: event.type,
          sessionLabel: event.sessionLabel,
          timestamp: event.timestamp || new Date(),
          data: event.data,
          source: 'mocha-multiple-sessions'
        };

        this.eventBus.emitSessionEvent(sessionEvent);
      });
    }
  }
}
