/**
 * GPS Recalibration Recovery Action
 * Demonstrates a concrete implementation of a recovery action for GPS systems
 */

import {
  BaseRecoveryAction,
  RecoveryActionConfig,
  RecoveryActionResult,
  RecoveryActionType,
  RecoveryPriority,
  RecoveryStatus
} from '../RecoveryAction';

import {
  HealthCheckResult,
  OperationalContext,
  OperationalContextType,
  MarineSystemType,
  MarineSystemTypeType,
  MarineEnvironmentStatus
} from '../../types';

/**
 * GPS recalibration parameters
 */
export interface GPSRecalibrationParams {
  /**
   * Reset satellite tracking
   */
  resetSatelliteTracking: boolean;
  
  /**
   * Clear almanac data
   */
  clearAlmanac: boolean;
  
  /**
   * Reset HDOP thresholds
   */
  resetHDOPThresholds: boolean;
  
  /**
   * Force cold start
   */
  forceColdStart: boolean;
  
  /**
   * Wait time for satellite acquisition in seconds
   */
  satelliteAcquisitionTimeout: number;
}

/**
 * GPS Recalibration Action
 * Attempts to recalibrate a GPS system that is reporting poor quality data
 */
export class GPSRecalibrationAction extends BaseRecoveryAction {
  private gpsInterface: {
    resetSatelliteTracking: () => Promise<boolean>;
    clearAlmanac: () => Promise<boolean>;
    resetHDOPThresholds: () => Promise<boolean>;
    forceColdStart: () => Promise<boolean>;
    waitForSatellites: (timeout: number) => Promise<number>;
  };
  
  constructor(
    config: RecoveryActionConfig,
    gpsInterface: {
      resetSatelliteTracking: () => Promise<boolean>;
      clearAlmanac: () => Promise<boolean>;
      resetHDOPThresholds: () => Promise<boolean>;
      forceColdStart: () => Promise<boolean>;
      waitForSatellites: (timeout: number) => Promise<number>;
    }
  ) {
    super(config);
    this.gpsInterface = gpsInterface;
  }
  
  /**
   * Perform GPS recalibration
   */
  protected async performRecovery(
    triggeringCheck: HealthCheckResult,
    operationalContext: OperationalContextType,
    marineEnvironment?: MarineEnvironmentStatus
  ): Promise<RecoveryActionResult> {
    const params = this.config.parameters as GPSRecalibrationParams;
    const startTime = new Date();
    const details: Record<string, any> = {};
    const steps: string[] = [];
    
    try {
      // Step 1: Reset satellite tracking if configured
      if (params.resetSatelliteTracking) {
        steps.push('Resetting satellite tracking');
        const resetResult = await this.gpsInterface.resetSatelliteTracking();
        details['resetSatelliteTracking'] = resetResult;
        
        if (!resetResult) {
          return this.createRecoveryResult(
            startTime,
            new Date(),
            false,
            'Failed to reset satellite tracking',
            steps,
            details,
            operationalContext,
            marineEnvironment
          );
        }
      }
      
      // Step 2: Clear almanac if configured
      if (params.clearAlmanac) {
        steps.push('Clearing almanac data');
        const clearResult = await this.gpsInterface.clearAlmanac();
        details['clearAlmanac'] = clearResult;
        
        if (!clearResult) {
          return this.createRecoveryResult(
            startTime,
            new Date(),
            false,
            'Failed to clear almanac data',
            steps,
            details,
            operationalContext,
            marineEnvironment
          );
        }
      }
      
      // Step 3: Reset HDOP thresholds if configured
      if (params.resetHDOPThresholds) {
        steps.push('Resetting HDOP thresholds');
        const resetResult = await this.gpsInterface.resetHDOPThresholds();
        details['resetHDOPThresholds'] = resetResult;
        
        if (!resetResult) {
          return this.createRecoveryResult(
            startTime,
            new Date(),
            false,
            'Failed to reset HDOP thresholds',
            steps,
            details,
            operationalContext,
            marineEnvironment
          );
        }
      }
      
      // Step 4: Force cold start if configured
      if (params.forceColdStart) {
        steps.push('Forcing cold start');
        const coldStartResult = await this.gpsInterface.forceColdStart();
        details['forceColdStart'] = coldStartResult;
        
        if (!coldStartResult) {
          return this.createRecoveryResult(
            startTime,
            new Date(),
            false,
            'Failed to force cold start',
            steps,
            details,
            operationalContext,
            marineEnvironment
          );
        }
      }
      
      // Step 5: Wait for satellites
      steps.push('Waiting for satellite acquisition');
      const satelliteCount = await this.gpsInterface.waitForSatellites(
        params.satelliteAcquisitionTimeout
      );
      details['satelliteCount'] = satelliteCount;
      
      // Check if we acquired enough satellites
      const success = satelliteCount >= 4; // Minimum for 3D fix
      const message = success
        ? `GPS recalibration successful, acquired ${satelliteCount} satellites`
        : `GPS recalibration failed, only acquired ${satelliteCount} satellites`;
      
      return this.createRecoveryResult(
        startTime,
        new Date(),
        success,
        message,
        steps,
        details,
        operationalContext,
        marineEnvironment
      );
      
    } catch (error) {
      return this.createRecoveryResult(
        startTime,
        new Date(),
        false,
        `GPS recalibration error: ${(error as Error).message}`,
        steps,
        details,
        operationalContext,
        marineEnvironment
      );
    }
  }
  
  /**
   * Create a recovery result
   */
  private createRecoveryResult(
    startTime: Date,
    endTime: Date,
    success: boolean,
    message: string,
    steps: string[],
    details: Record<string, any>,
    operationalContext: OperationalContextType,
    marineEnvironment?: MarineEnvironmentStatus
  ): RecoveryActionResult {
    return {
      actionId: this.config.actionId,
      name: this.config.name,
      type: this.config.type,
      status: success ? RecoveryStatus.SUCCEEDED : RecoveryStatus.FAILED,
      message,
      startTime,
      endTime,
      duration: endTime.getTime() - startTime.getTime(),
      retryCount: this.retryCount,
      success,
      error: success ? '' : message,
      details: {
        ...details,
        steps,
        completedSteps: steps.length
      },
      marineContext: {
        operationalContext,
        environmentalConditions: marineEnvironment ? { ...marineEnvironment } : undefined
      },
      triggeringCheck: undefined
    };
  }
}

/**
 * Create a GPS recalibration action with default configuration
 */
export function createGPSRecalibrationAction(
  gpsInterface: {
    resetSatelliteTracking: () => Promise<boolean>;
    clearAlmanac: () => Promise<boolean>;
    resetHDOPThresholds: () => Promise<boolean>;
    forceColdStart: () => Promise<boolean>;
    waitForSatellites: (timeout: number) => Promise<number>;
  },
  customConfig?: Partial<RecoveryActionConfig>
): GPSRecalibrationAction {
  const defaultConfig: RecoveryActionConfig = {
    actionId: 'gps-recalibration',
    name: 'GPS Recalibration',
    type: RecoveryActionType.RECALIBRATE,
    marineSystemType: MarineSystemType.NAVIGATION as MarineSystemTypeType,
    priority: RecoveryPriority.HIGH,
    timeout: 60000, // 60 seconds
    maxRetries: 1,
    retryDelay: 5000,
    requiresApproval: false,
    automatedExecution: true,
    marineSettings: {
      allowedContexts: [
        OperationalContext.DOCKED,
        OperationalContext.ANCHORED,
        OperationalContext.SAILING,
        OperationalContext.MOTORING
      ] as OperationalContextType[],
      allowInRoughSeas: false,
      powerAware: true,
      safetyImpact: true,
      powerConsumption: 2 // 2 watts
    },
    parameters: {
      resetSatelliteTracking: true,
      clearAlmanac: false,
      resetHDOPThresholds: true,
      forceColdStart: false,
      satelliteAcquisitionTimeout: 30 // 30 seconds
    }
  };
  
  return new GPSRecalibrationAction(
    { ...defaultConfig, ...customConfig },
    gpsInterface
  );
}
