/**
 * submission-engine.ts — 12-Step A2P Registration State Machine
 * Orchestrates the entire Twilio TrustHub + A2P submission flow with retry logic
 */

import pino from 'pino';
import type {
  RegistrationInput,
  SubmissionRecord,
  SubmissionStatus,
  SidChain,
} from '../types';
import { TwilioApiClient, type TwilioConfig } from './twilio-client';
import { validateRegistrationInput, validateSidChainForStep } from './validators';

const logger = pino({ name: 'submission-engine' });

// ============================================================
// ENGINE CONFIGURATION
// ============================================================

export interface EngineConfig {
  twilio: TwilioConfig;
  maxRetries?: number;           // Max retries per step (default: 3)
  retryDelayMs?: number;         // Base delay between retries (default: 2000ms)
  pollIntervalMs?: number;       // Interval for status polling (default: 30000ms)
  pollTimeoutMs?: number;        // Max time to wait for approval (default: 3600000ms = 1hr)
}

export interface StepResult {
  success: boolean;
  sidChain?: Partial<SidChain>;
  status?: SubmissionStatus;
  error?: string;
  shouldRetry?: boolean;
}

// ============================================================
// SUBMISSION ENGINE
// ============================================================

export class SubmissionEngine {
  private client: TwilioApiClient;
  private config: EngineConfig;

  constructor(config: EngineConfig) {
    this.config = {
      maxRetries: 3,
      retryDelayMs: 2000,
      pollIntervalMs: 30000,
      pollTimeoutMs: 3600000,
      ...config,
    };
    this.client = new TwilioApiClient(config.twilio);
    logger.info('SubmissionEngine initialized');
  }

  // ============================================================
  // MAIN ORCHESTRATION
  // ============================================================

  /**
   * Executes the complete 12-step A2P registration flow
   * Updates the SubmissionRecord at each step
   */
  async executeSubmission(
    record: SubmissionRecord,
    onUpdate?: (record: SubmissionRecord) => Promise<void>
  ): Promise<SubmissionRecord> {
    try {
      logger.info({ id: record.id }, 'Starting A2P submission');

      // Validate input before starting
      const validation = validateRegistrationInput(record.input);
      if (!validation.success) {
        logger.error({ errors: validation.errors }, 'Input validation failed');
        record.status = 'manual_review';
        record.failureReason = `Validation failed: ${JSON.stringify(validation.errors)}`;
        if (onUpdate) await onUpdate(record);
        return record;
      }

      // Execute steps sequentially
      const steps = [
        { num: 1, fn: this.step1CreateSecondaryProfile.bind(this), status: 'creating_profile' as SubmissionStatus },
        { num: 2, fn: this.step2CreateBusinessEndUser.bind(this), status: 'creating_profile' as SubmissionStatus },
        { num: 3, fn: this.step3CreateAuthorizedRepEndUser.bind(this), status: 'creating_profile' as SubmissionStatus },
        { num: 4, fn: this.step4CreateAddressAndDocument.bind(this), status: 'creating_profile' as SubmissionStatus },
        { num: 5, fn: this.step5AssignSecondaryToPrimary.bind(this), status: 'creating_profile' as SubmissionStatus },
        { num: 6, fn: this.step6EvaluateProfile.bind(this), status: 'creating_profile' as SubmissionStatus },
        { num: 7, fn: this.step7SubmitProfile.bind(this), status: 'profile_submitted' as SubmissionStatus },
        { num: 8, fn: this.step8CreateTrustProduct.bind(this), status: 'creating_brand' as SubmissionStatus },
        { num: 9, fn: this.step9CreateBrandRegistration.bind(this), status: 'brand_pending' as SubmissionStatus },
        { num: 10, fn: this.step10CreateMessagingService.bind(this), status: 'creating_campaign' as SubmissionStatus },
        { num: 11, fn: this.step11CreateCampaign.bind(this), status: 'campaign_pending' as SubmissionStatus },
        { num: 12, fn: this.step12AssignPhoneNumbers.bind(this), status: 'campaign_pending' as SubmissionStatus },
      ];

      for (const step of steps) {
        logger.info({ step: step.num }, `Executing step ${step.num}`);
        
        // Update status before executing step
        record.status = step.status;
        record.updatedAt = new Date();
        if (onUpdate) await onUpdate(record);

        // Execute step with retry logic
        const result = await this.executeStepWithRetry(
          step.num,
          () => step.fn(record.input, record.sidChain)
        );

        if (!result.success) {
          logger.error({ step: step.num, error: result.error }, `Step ${step.num} failed`);
          record.status = result.shouldRetry ? 'remediation' : 'manual_review';
          record.failureReason = result.error;
          record.attemptCount++;
          if (onUpdate) await onUpdate(record);
          return record;
        }

        // Update SID chain
        if (result.sidChain) {
          record.sidChain = { ...record.sidChain, ...result.sidChain };
        }

        // Update status if provided
        if (result.status) {
          record.status = result.status;
        }

        record.updatedAt = new Date();
        if (onUpdate) await onUpdate(record);
      }

      // Poll for final campaign approval
      logger.info('Polling for campaign approval');
      const approved = await this.pollCampaignApproval(
        record.sidChain.brandRegistrationSid!,
        record.sidChain.campaignSid!
      );

      if (approved) {
        record.status = 'campaign_approved';
        record.completedAt = new Date();
        logger.info({ id: record.id }, 'Submission completed successfully');
      } else {
        record.status = 'campaign_failed';
        record.failureReason = 'Campaign approval timeout';
        logger.error({ id: record.id }, 'Campaign approval timeout');
      }

      record.updatedAt = new Date();
      if (onUpdate) await onUpdate(record);

      return record;
    } catch (error: any) {
      logger.error({ error: error.message }, 'Submission execution failed');
      record.status = 'manual_review';
      record.failureReason = error.message;
      record.updatedAt = new Date();
      if (onUpdate) await onUpdate(record);
      return record;
    }
  }

  // ============================================================
  // STEP 1: CREATE SECONDARY CUSTOMER PROFILE
  // ============================================================

  private async step1CreateSecondaryProfile(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Check if already exists
      if (sidChain.customerProfileSid) {
        logger.info({ sid: sidChain.customerProfileSid }, 'CustomerProfile already exists');
        return { success: true };
      }

      const result = await this.client.createSecondaryCustomerProfile(
        input.business.businessName,
        input.authorizedRep.email
      );

      return {
        success: true,
        sidChain: { customerProfileSid: result.sid },
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 1 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 2: CREATE BUSINESS END USER
  // ============================================================

  private async step2CreateBusinessEndUser(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 2);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Check if already exists
      if (sidChain.businessEndUserSid) {
        logger.info({ sid: sidChain.businessEndUserSid }, 'Business EndUser already exists');
        return { success: true };
      }

      // Create EndUser
      const endUser = await this.client.createBusinessEndUser(input.business);

      // Assign to CustomerProfile
      await this.client.assignEndUserToProfile(
        sidChain.customerProfileSid!,
        endUser.sid
      );

      return {
        success: true,
        sidChain: { businessEndUserSid: endUser.sid },
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 2 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 3: CREATE AUTHORIZED REP END USER
  // ============================================================

  private async step3CreateAuthorizedRepEndUser(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 3);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Check if already exists
      if (sidChain.authorizedRepEndUserSid) {
        logger.info({ sid: sidChain.authorizedRepEndUserSid }, 'Authorized Rep EndUser already exists');
        return { success: true };
      }

      // Create EndUser
      const endUser = await this.client.createAuthorizedRepEndUser(input.authorizedRep);

      // Assign to CustomerProfile
      await this.client.assignEndUserToProfile(
        sidChain.customerProfileSid!,
        endUser.sid
      );

      return {
        success: true,
        sidChain: { authorizedRepEndUserSid: endUser.sid },
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 3 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 4: CREATE ADDRESS + SUPPORTING DOCUMENT
  // ============================================================

  private async step4CreateAddressAndDocument(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 4);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Check if already exists
      if (sidChain.addressSid && sidChain.supportingDocSid) {
        logger.info('Address and SupportingDocument already exist');
        return { success: true };
      }

      // Create Address
      const address = await this.client.createAddress(input.address);

      // Create SupportingDocument from Address
      const doc = await this.client.createAddressSupportingDocument(
        address.sid,
        input.business.businessName
      );

      // Assign SupportingDocument to CustomerProfile
      await this.client.assignSupportingDocToProfile(
        sidChain.customerProfileSid!,
        doc.sid
      );

      return {
        success: true,
        sidChain: {
          addressSid: address.sid,
          supportingDocSid: doc.sid,
        },
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 4 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 5: ASSIGN SECONDARY TO PRIMARY
  // ============================================================

  private async step5AssignSecondaryToPrimary(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 5);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      await this.client.assignSecondaryToPrimary(sidChain.customerProfileSid!);

      return { success: true };
    } catch (error: any) {
      // If already assigned, this might fail - check if it's a "duplicate" error
      if (error.message.includes('already assigned') || error.message.includes('duplicate')) {
        logger.info('Secondary already assigned to Primary');
        return { success: true };
      }

      return {
        success: false,
        error: `Step 5 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 6: EVALUATE CUSTOMER PROFILE
  // ============================================================

  private async step6EvaluateProfile(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 6);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      const evaluation = await this.client.evaluateCustomerProfile(
        sidChain.customerProfileSid!
      );

      // Check if evaluation passed
      if (evaluation.status === 'compliant') {
        return { success: true };
      }

      // Log non-compliant results
      logger.warn({ results: evaluation.results }, 'CustomerProfile evaluation not compliant');
      
      return {
        success: false,
        error: `CustomerProfile evaluation failed: ${JSON.stringify(evaluation.results)}`,
        shouldRetry: false, // Evaluation failures usually need manual remediation
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 6 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 7: SUBMIT CUSTOMER PROFILE
  // ============================================================

  private async step7SubmitProfile(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 7);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Check if already approved
      const isApproved = await this.client.isCustomerProfileApproved(
        sidChain.customerProfileSid!
      );

      if (isApproved) {
        logger.info('CustomerProfile already approved');
        return { success: true, status: 'profile_submitted' };
      }

      await this.client.submitCustomerProfile(sidChain.customerProfileSid!);

      return {
        success: true,
        status: 'profile_submitted',
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 7 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 8: CREATE TRUST PRODUCT
  // ============================================================

  private async step8CreateTrustProduct(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 8);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Check if already exists
      if (sidChain.trustProductSid && sidChain.a2pProfileEndUserSid) {
        logger.info('TrustProduct and A2P Profile already exist');
        return { success: true };
      }

      // Create TrustProduct
      const trustProduct = await this.client.createTrustProduct(
        input.business.businessName,
        input.authorizedRep.email
      );

      // Create A2P Profile EndUser
      const a2pEndUser = await this.client.createA2PProfileEndUser(input.business);

      // Assign EndUser to TrustProduct
      await this.client.assignEndUserToTrustProduct(
        trustProduct.sid,
        a2pEndUser.sid
      );

      // Submit TrustProduct
      await this.client.submitTrustProduct(trustProduct.sid);

      return {
        success: true,
        sidChain: {
          trustProductSid: trustProduct.sid,
          a2pProfileEndUserSid: a2pEndUser.sid,
        },
        status: 'creating_brand',
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 8 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 9: CREATE BRAND REGISTRATION
  // ============================================================

  private async step9CreateBrandRegistration(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 9);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Check if already exists
      if (sidChain.brandRegistrationSid) {
        logger.info({ sid: sidChain.brandRegistrationSid }, 'Brand Registration already exists');
        return { success: true };
      }

      // Wait for TrustProduct approval first
      const trustProductApproved = await this.pollTrustProductApproval(
        sidChain.trustProductSid!
      );

      if (!trustProductApproved) {
        return {
          success: false,
          error: 'TrustProduct approval timeout',
          shouldRetry: false,
        };
      }

      // Create Brand Registration
      const brand = await this.client.createBrandRegistration(
        sidChain.trustProductSid!,
        input.business.skipAutoSecVet
      );

      return {
        success: true,
        sidChain: { brandRegistrationSid: brand.sid },
        status: 'brand_pending',
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 9 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 10: CREATE MESSAGING SERVICE
  // ============================================================

  private async step10CreateMessagingService(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 10);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Use existing messaging service if provided
      if (input.phone.messagingServiceSid) {
        logger.info({ sid: input.phone.messagingServiceSid }, 'Using existing Messaging Service');
        return {
          success: true,
          sidChain: { messagingServiceSid: input.phone.messagingServiceSid },
        };
      }

      // Check if already created
      if (sidChain.messagingServiceSid) {
        logger.info({ sid: sidChain.messagingServiceSid }, 'Messaging Service already exists');
        return { success: true };
      }

      // Create new Messaging Service
      const service = await this.client.createMessagingService(
        `${input.business.businessName} - A2P`
      );

      // Assign phone numbers if provided
      if (input.phone.phoneNumbers && input.phone.phoneNumbers.length > 0) {
        await this.client.assignPhoneNumbersToService(
          service.sid,
          input.phone.phoneNumbers
        );
      }

      return {
        success: true,
        sidChain: { messagingServiceSid: service.sid },
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 10 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 11: CREATE A2P CAMPAIGN
  // ============================================================

  private async step11CreateCampaign(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 11);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Check if already exists
      if (sidChain.campaignSid) {
        logger.info({ sid: sidChain.campaignSid }, 'Campaign already exists');
        return { success: true };
      }

      // Wait for Brand approval first
      const brandApproved = await this.pollBrandApproval(
        sidChain.brandRegistrationSid!
      );

      if (!brandApproved) {
        return {
          success: false,
          error: 'Brand approval timeout',
          shouldRetry: false,
        };
      }

      // Create Campaign
      const campaign = await this.client.createUsAppToPersonCampaign(
        sidChain.brandRegistrationSid!,
        sidChain.messagingServiceSid!,
        input.campaign,
        input.campaign.messageFlow,
        input.campaign.optInMessage,
        input.campaign.optOutMessage,
        input.campaign.helpMessage
      );

      return {
        success: true,
        sidChain: { campaignSid: campaign.sid },
        status: 'campaign_pending',
      };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 11 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // STEP 12: ASSIGN PHONE NUMBERS TO CAMPAIGN
  // ============================================================

  private async step12AssignPhoneNumbers(
    input: RegistrationInput,
    sidChain: SidChain
  ): Promise<StepResult> {
    try {
      // Validate prerequisites
      const validation = validateSidChainForStep(sidChain, 12);
      if (!validation.valid) {
        return {
          success: false,
          error: `Missing required SIDs: ${validation.missing?.join(', ')}`,
          shouldRetry: false,
        };
      }

      // Phone numbers are automatically associated via Messaging Service
      // This step is mainly a confirmation
      if (input.phone.phoneNumbers && input.phone.phoneNumbers.length > 0) {
        await this.client.assignPhoneNumbersToCampaign(
          sidChain.messagingServiceSid!,
          input.phone.phoneNumbers
        );
      }

      logger.info('Phone numbers assigned/confirmed');
      return { success: true };
    } catch (error: any) {
      return {
        success: false,
        error: `Step 12 failed: ${error.message}`,
        shouldRetry: true,
      };
    }
  }

  // ============================================================
  // RETRY LOGIC
  // ============================================================

  /**
   * Executes a step with exponential backoff retry logic
   */
  private async executeStepWithRetry(
    stepNum: number,
    stepFn: () => Promise<StepResult>
  ): Promise<StepResult> {
    const maxRetries = this.config.maxRetries || 3;
    let attempt = 0;
    let lastError: string = '';

    while (attempt < maxRetries) {
      try {
        const result = await stepFn();
        
        if (result.success) {
          return result;
        }

        lastError = result.error || 'Unknown error';

        // Don't retry if explicitly told not to
        if (!result.shouldRetry) {
          return result;
        }

        attempt++;
        if (attempt < maxRetries) {
          const delay = (this.config.retryDelayMs || 2000) * Math.pow(2, attempt - 1);
          logger.warn(
            { step: stepNum, attempt, delay },
            `Step ${stepNum} failed, retrying in ${delay}ms`
          );
          await this.sleep(delay);
        }
      } catch (error: any) {
        lastError = error.message;
        attempt++;
        
        if (attempt < maxRetries) {
          const delay = (this.config.retryDelayMs || 2000) * Math.pow(2, attempt - 1);
          logger.warn(
            { step: stepNum, attempt, delay, error: error.message },
            `Step ${stepNum} threw exception, retrying in ${delay}ms`
          );
          await this.sleep(delay);
        }
      }
    }

    return {
      success: false,
      error: `Step ${stepNum} failed after ${maxRetries} attempts: ${lastError}`,
      shouldRetry: false,
    };
  }

  // ============================================================
  // POLLING HELPERS
  // ============================================================

  /**
   * Polls for TrustProduct approval
   */
  private async pollTrustProductApproval(trustProductSid: string): Promise<boolean> {
    const startTime = Date.now();
    const timeout = this.config.pollTimeoutMs || 3600000;
    const interval = this.config.pollIntervalMs || 30000;

    logger.info({ trustProductSid }, 'Polling for TrustProduct approval');

    while (Date.now() - startTime < timeout) {
      const approved = await this.client.isTrustProductApproved(trustProductSid);
      
      if (approved) {
        logger.info({ trustProductSid }, 'TrustProduct approved');
        return true;
      }

      logger.info('TrustProduct not yet approved, waiting...');
      await this.sleep(interval);
    }

    logger.error({ trustProductSid }, 'TrustProduct approval timeout');
    return false;
  }

  /**
   * Polls for Brand Registration approval
   */
  private async pollBrandApproval(brandSid: string): Promise<boolean> {
    const startTime = Date.now();
    const timeout = this.config.pollTimeoutMs || 3600000;
    const interval = this.config.pollIntervalMs || 30000;

    logger.info({ brandSid }, 'Polling for Brand approval');

    while (Date.now() - startTime < timeout) {
      const status = await this.client.getBrandRegistrationStatus(brandSid);
      
      if (status.identityStatus === 'VERIFIED' || status.identityStatus === 'SELF_DECLARED') {
        logger.info({ brandSid, status: status.identityStatus }, 'Brand approved');
        return true;
      }

      if (status.identityStatus === 'FAILED' || status.identityStatus === 'REJECTED') {
        logger.error({ brandSid, status: status.identityStatus }, 'Brand registration failed');
        return false;
      }

      logger.info({ status: status.identityStatus }, 'Brand not yet approved, waiting...');
      await this.sleep(interval);
    }

    logger.error({ brandSid }, 'Brand approval timeout');
    return false;
  }

  /**
   * Polls for Campaign approval
   */
  private async pollCampaignApproval(brandSid: string, campaignSid: string): Promise<boolean> {
    const startTime = Date.now();
    const timeout = this.config.pollTimeoutMs || 3600000;
    const interval = this.config.pollIntervalMs || 30000;

    logger.info({ brandSid, campaignSid }, 'Polling for Campaign approval');

    while (Date.now() - startTime < timeout) {
      const status = await this.client.getCampaignStatus(brandSid, campaignSid);
      
      if (status.status === 'active' || status.status === 'APPROVED') {
        logger.info({ campaignSid, status: status.status }, 'Campaign approved');
        return true;
      }

      if (status.status === 'failed' || status.status === 'REJECTED') {
        logger.error({ campaignSid, status: status.status }, 'Campaign failed');
        return false;
      }

      logger.info({ status: status.status }, 'Campaign not yet approved, waiting...');
      await this.sleep(interval);
    }

    logger.error({ campaignSid }, 'Campaign approval timeout');
    return false;
  }

  /**
   * Sleep helper
   */
  private sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
}
