/**
 * Bulkhead manager for coordinating multiple resource pools
 */

import { ResourcePool, ResourceRequest, ResourceAllocation } from './ResourcePool';
import {
  BulkheadConfig,
  ResourcePoolMetrics,
  ResilienceEvent,
  MarineEnvironmentStatus
} from '../types';

import {
  OperationalContext,
  OperationalContextType
} from '../types/marine-constants';

/**
 * Bulkhead execution context
 */
export interface BulkheadExecutionContext {
  poolName: string;
  priority: 'critical' | 'normal' | 'low';
  marineSystemType: 'navigation' | 'safety' | 'comfort' | 'maintenance';
  operationalContext: OperationalContextType;
  timeoutMs?: number;
}

/**
 * Bulkhead execution result
 */
export interface BulkheadExecutionResult<T> {
  success: boolean;
  data?: T;
  error?: Error;
  resourceAllocation: ResourceAllocation;
  poolMetrics: ResourcePoolMetrics;
  executionTime: number;
}

/**
 * Manager for coordinating multiple bulkhead resource pools
 */
export class BulkheadManager {
  private static instance: BulkheadManager | null = null;
  private pools: Map<string, ResourcePool> = new Map();
  private eventListeners: ((event: ResilienceEvent) => void)[] = [];
  private marineEnvironment?: MarineEnvironmentStatus;

  /**
   * Get the singleton instance of BulkheadManager
   * This ensures only one instance exists across the application
   */
  public static getInstance(): BulkheadManager {
    if (!BulkheadManager.instance) {
      BulkheadManager.instance = new BulkheadManager();
    }
    return BulkheadManager.instance;
  }

  constructor() {
    // Initialize with default marine resource pools
    this.initializeDefaultPools();
  }

  /**
   * Execute operation with bulkhead protection
   */
  async execute<T>(
    operation: () => Promise<T>,
    context: BulkheadExecutionContext
  ): Promise<BulkheadExecutionResult<T>> {
    const startTime = Date.now();
    const pool = this.getPool(context.poolName);

    if (!pool) {
      return {
        success: false,
        error: new Error(`Resource pool '${context.poolName}' not found`),
        resourceAllocation: {
          granted: false,
          waitTime: 0,
          reason: 'Pool not found'
        },
        poolMetrics: this.getEmptyMetrics(context.poolName),
        executionTime: Date.now() - startTime
      };
    }

    // Request resource from pool
    const resourceRequest: Omit<ResourceRequest, 'id' | 'requestedAt'> = {
      priority: context.priority,
      operationalContext: context.operationalContext,
      timeoutMs: context.timeoutMs || 30000, // Default 30 seconds
      marineSystemType: context.marineSystemType
    };

    const allocation = await pool.requestResource(resourceRequest);

    if (!allocation.granted) {
      return {
        success: false,
        error: new Error(`Resource allocation failed: ${allocation.reason}`),
        resourceAllocation: allocation,
        poolMetrics: pool.getMetrics(),
        executionTime: Date.now() - startTime
      };
    }

    // Execute operation with resource
    try {
      const result = await operation();
      const executionTime = Date.now() - startTime;

      // Release resource
      if (allocation.resourceId) {
        pool.releaseResource(allocation.resourceId);
      }

      return {
        success: true,
        data: result,
        resourceAllocation: allocation,
        poolMetrics: pool.getMetrics(),
        executionTime
      };

    } catch (error) {
      const executionTime = Date.now() - startTime;

      // Release resource on error
      if (allocation.resourceId) {
        pool.releaseResource(allocation.resourceId);
      }

      return {
        success: false,
        error: error as Error,
        resourceAllocation: allocation,
        poolMetrics: pool.getMetrics(),
        executionTime
      };
    }
  }

  /**
   * Create or update a resource pool
   */
  createPool(config: BulkheadConfig): void {
    const pool = new ResourcePool(config);
    
    // Forward events from pool
    pool.onEvent((event) => {
      this.emitEvent(event);
    });

    // Update with current marine environment
    if (this.marineEnvironment) {
      pool.updateMarineEnvironment(this.marineEnvironment);
    }

    this.pools.set(config.poolName, pool);
  }

  /**
   * Get resource pool by name
   */
  getPool(poolName: string): ResourcePool | undefined {
    return this.pools.get(poolName);
  }

  /**
   * Get all pool metrics
   */
  getAllPoolMetrics(): Record<string, ResourcePoolMetrics> {
    const metrics: Record<string, ResourcePoolMetrics> = {};
    
    for (const [poolName, pool] of this.pools) {
      metrics[poolName] = pool.getMetrics();
    }

    return metrics;
  }

  /**
   * Get system-wide bulkhead status
   */
  getSystemStatus() {
    const poolStatuses: Record<string, any> = {};
    let totalCapacity = 0;
    let totalActive = 0;
    let totalQueued = 0;
    let totalRejected = 0;
    let totalPowerImpact = 0;

    for (const [poolName, pool] of this.pools) {
      const status = pool.getStatus();
      poolStatuses[poolName] = status;
      
      totalCapacity += status.metrics.totalCapacity;
      totalActive += status.metrics.activeRequests;
      totalQueued += status.metrics.queuedRequests;
      totalRejected += status.metrics.rejectedRequests;
      totalPowerImpact += status.metrics.powerImpact;
    }

    return {
      pools: poolStatuses,
      systemTotals: {
        totalCapacity,
        totalActive,
        totalQueued,
        totalRejected,
        totalPowerImpact,
        utilizationPercentage: totalCapacity > 0 ? (totalActive / totalCapacity) * 100 : 0
      },
      marineEnvironment: this.marineEnvironment
    };
  }

  /**
   * Update marine environment for all pools
   */
  updateMarineEnvironment(environment: MarineEnvironmentStatus): void {
    this.marineEnvironment = environment;
    
    for (const pool of this.pools.values()) {
      pool.updateMarineEnvironment(environment);
    }

    // Emit environment update event
    this.emitEvent({
      timestamp: new Date(),
      eventType: 'recovery',
      component: 'bulkhead-manager',
      details: {
        action: 'marine_environment_updated',
        seaState: environment.seaState,
        powerStatus: environment.powerStatus,
        criticalOperationsOnly: environment.criticalOperationsOnly
      },
      severity: 'info',
      marineContext: {
        operationalContext: environment.powerStatus === 'critical' ? 
          OperationalContext.EMERGENCY : OperationalContext.SAILING,
        environmentalImpact: environment.seaState === 'very_rough',
        safetyImpact: environment.criticalOperationsOnly
      }
    });
  }

  /**
   * Emergency release all resources across all pools
   */
  emergencyReleaseAll(reason: string = 'Emergency shutdown'): void {
    let totalReleased = 0;

    for (const [poolName, pool] of this.pools) {
      const status = pool.getStatus();
      const activeCount = status.metrics.activeRequests;
      
      pool.forceReleaseAll(reason);
      totalReleased += activeCount;
    }

    this.emitEvent({
      timestamp: new Date(),
      eventType: 'recovery',
      component: 'bulkhead-manager',
      details: {
        action: 'emergency_release_all',
        totalResourcesReleased: totalReleased,
        reason
      },
      severity: 'critical',
      marineContext: {
        operationalContext: OperationalContext.EMERGENCY,
        environmentalImpact: true,
        safetyImpact: true
      }
    });
  }

  /**
   * Add event listener
   */
  onEvent(listener: (event: ResilienceEvent) => void): void {
    this.eventListeners.push(listener);
  }

  /**
   * Remove a resource pool
   */
  removePool(poolName: string): boolean {
    const pool = this.pools.get(poolName);
    if (pool) {
      // Force release all resources before removing
      pool.forceReleaseAll('Pool being removed');
      this.pools.delete(poolName);
      return true;
    }
    return false;
  }

  /**
   * Get pool names
   */
  getPoolNames(): string[] {
    return Array.from(this.pools.keys());
  }

  /**
   * Check if pool exists
   */
  hasPool(poolName: string): boolean {
    return this.pools.has(poolName);
  }

  /**
   * Initialize default marine resource pools
   */
  private initializeDefaultPools(): void {
    // Navigation pool - critical for vessel safety
    this.createPool({
      poolName: 'navigation',
      maxConcurrentRequests: 8,
      maxWaitTime: 5000, // 5 seconds
      priority: 'critical',
      marineSystemType: 'navigation',
      powerConsumption: 'medium',
      operationalDependency: 'essential'
    });

    // Safety pool - highest priority
    this.createPool({
      poolName: 'safety',
      maxConcurrentRequests: 6,
      maxWaitTime: 3000, // 3 seconds
      priority: 'critical',
      marineSystemType: 'safety',
      powerConsumption: 'low',
      operationalDependency: 'essential'
    });

    // Comfort pool - lower priority, can be throttled
    this.createPool({
      poolName: 'comfort',
      maxConcurrentRequests: 4,
      maxWaitTime: 15000, // 15 seconds
      priority: 'low',
      marineSystemType: 'comfort',
      powerConsumption: 'high',
      operationalDependency: 'optional'
    });

    // Maintenance pool - for system maintenance operations
    this.createPool({
      poolName: 'maintenance',
      maxConcurrentRequests: 2,
      maxWaitTime: 60000, // 1 minute
      priority: 'normal',
      marineSystemType: 'maintenance',
      powerConsumption: 'high',
      operationalDependency: 'important'
    });

    // Communication pool - for network operations
    this.createPool({
      poolName: 'communication',
      maxConcurrentRequests: 3,
      maxWaitTime: 30000, // 30 seconds
      priority: 'normal',
      marineSystemType: 'comfort', // Communication is comfort/convenience
      powerConsumption: 'medium',
      operationalDependency: 'important'
    });
  }

  /**
   * Emit resilience event
   */
  private emitEvent(event: ResilienceEvent): void {
    this.eventListeners.forEach(listener => {
      try {
        listener(event);
      } catch (error) {
        console.error('Error in bulkhead manager event listener:', error);
      }
    });
  }

  /**
   * Get empty metrics for error cases
   */
  private getEmptyMetrics(poolName: string): ResourcePoolMetrics {
    return {
      poolName,
      totalCapacity: 0,
      availableResources: 0,
      activeRequests: 0,
      queuedRequests: 0,
      rejectedRequests: 0,
      averageWaitTime: 0,
      maxWaitTime: 0,
      powerImpact: 0,
      criticalRequestsActive: 0,
      systemHealthImpact: 0
    };
  }
}

/**
 * Default bulkhead manager instance
 * Using the getInstance method ensures we always get the same instance
 */
export const defaultBulkheadManager = BulkheadManager.getInstance();

/**
 * Convenience function to execute with bulkhead protection
 * Always uses the singleton instance to ensure consistency
 */
export async function executeWithBulkhead<T>(
  operation: () => Promise<T>,
  context: BulkheadExecutionContext
): Promise<BulkheadExecutionResult<T>> {
  return BulkheadManager.getInstance().execute(operation, context);
}

/**
 * Pre-configured bulkhead execution contexts
 */
export const BulkheadContexts = {
  /**
   * Navigation operations (GPS, compass, autopilot)
   */
  navigation: (operationalContext: OperationalContextType = OperationalContext.SAILING): BulkheadExecutionContext => ({
    poolName: 'navigation',
    priority: 'critical',
    marineSystemType: 'navigation',
    operationalContext,
    timeoutMs: 5000
  }),

  /**
   * Safety operations (anchor alarm, collision avoidance)
   */
  safety: (operationalContext: OperationalContextType = OperationalContext.SAILING): BulkheadExecutionContext => ({
    poolName: 'safety',
    priority: 'critical',
    marineSystemType: 'safety',
    operationalContext,
    timeoutMs: 3000
  }),

  /**
   * Comfort operations (lighting, entertainment, climate)
   */
  comfort: (operationalContext: OperationalContextType = OperationalContext.SAILING): BulkheadExecutionContext => ({
    poolName: 'comfort',
    priority: 'low',
    marineSystemType: 'comfort',
    operationalContext,
    timeoutMs: 15000
  }),

  /**
   * Maintenance operations (diagnostics, updates)
   */
  maintenance: (operationalContext: OperationalContextType = OperationalContext.MAINTENANCE): BulkheadExecutionContext => ({
    poolName: 'maintenance',
    priority: 'normal',
    marineSystemType: 'maintenance',
    operationalContext,
    timeoutMs: 60000
  }),

  /**
   * Communication operations (weather, email, messaging)
   */
  communication: (operationalContext: OperationalContextType = OperationalContext.SAILING): BulkheadExecutionContext => ({
    poolName: 'communication',
    priority: 'normal',
    marineSystemType: 'comfort',
    operationalContext,
    timeoutMs: 30000
  })
};
