import type {
  Channel,
  NormalizedMessage,
  ResolvedCredentials,
  ProviderErrorCode,
} from '@zyphon/shared-types';
import type {
  ProviderAdapter,
  ProviderResult,
  ProviderHealth,
  WebhookRequest,
  DeliveryEvent,
} from '@zyphon/provider-contracts';

export interface MockProviderOptions {
  id: string;
  channel: Channel;
  latencyMs?: number;
  failureRate?: number; // 0.0 to 1.0
  forcedFailure?: boolean;
  forcedErrorCode?: ProviderErrorCode;
}

export class MockProvider implements ProviderAdapter {
  public readonly id: string;
  public readonly channel: Channel;
  public latencyMs: number;
  public failureRate: number;
  public forcedFailure: boolean;
  public forcedErrorCode: ProviderErrorCode;
  public messageLog: NormalizedMessage[] = [];

  constructor(options: MockProviderOptions) {
    this.id = options.id;
    this.channel = options.channel;
    this.latencyMs = options.latencyMs ?? 15;
    this.failureRate = options.failureRate ?? 0;
    this.forcedFailure = options.forcedFailure ?? false;
    this.forcedErrorCode = options.forcedErrorCode ?? 'PROVIDER_5XX';
  }

  public async validateConfiguration(_c: ResolvedCredentials): Promise<void> {
    // Mock always accepts configuration
  }

  public async send(
    m: NormalizedMessage,
    _c: ResolvedCredentials,
    signal: AbortSignal
  ): Promise<ProviderResult> {
    if (signal.aborted) {
      return {
        success: false,
        error: { code: 'TIMEOUT', message: 'Send operation aborted by caller' },
      };
    }

    if (this.latencyMs > 0) {
      await new Promise((resolve) => setTimeout(resolve, this.latencyMs));
    }

    if (signal.aborted) {
      return {
        success: false,
        error: { code: 'TIMEOUT', message: 'Send operation aborted after latency' },
      };
    }

    if (this.forcedFailure || Math.random() < this.failureRate) {
      return {
        success: false,
        error: {
          code: this.forcedErrorCode,
          message: `Simulated error for ${this.id}`,
        },
      };
    }

    this.messageLog.push(m);

    return {
      success: true,
      provider_reference: `mock_ref_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
      raw_response: {
        mock: true,
        channel: this.channel,
        sent_at: Date.now(),
      },
    };
  }

  public async healthCheck(_c: ResolvedCredentials): Promise<ProviderHealth> {
    const isDown = this.forcedFailure || this.failureRate >= 1.0;
    return {
      status: isDown ? 'down' : 'healthy',
      latency_ms: this.latencyMs,
      checked_at: Date.now(),
      message: isDown ? `Provider ${this.id} simulated outage` : `Provider ${this.id} is healthy`,
    };
  }

  public parseWebhook(req: WebhookRequest): DeliveryEvent | null {
    const body = req.body as any;
    if (body?.message_id && body?.status) {
      return {
        message_id: body.message_id,
        status: body.status === 'delivered' ? 'delivered' : 'undelivered',
        timestamp: Date.now(),
        provider: this.id,
        raw: body,
      };
    }
    return null;
  }
}

