import { CarrierApi, CarrierCall, CarrierSms } from './carrier-types';

interface PlivoConfig {
  authId: string;
  authToken: string;
  apiBaseUrl?: string;
  decisionTimeout?: number;
}

export class PlivoCarrier implements CarrierApi {
  private readonly config: PlivoConfig;
  private readonly apiBaseUrl: string;

  constructor(config: PlivoConfig) {
    this.config = {
      ...config,
      apiBaseUrl: config.apiBaseUrl ?? 'https://api.plivo.com',
      decisionTimeout: config.decisionTimeout ?? 200,
    };
    this.apiBaseUrl = this.config.apiBaseUrl;
  }

  async getCall(callSid: string): Promise<CarrierCall> {
    const response = await fetch(
      `${this.apiBaseUrl}/v1/Account/${this.config.authId}/Call/${callSid}/`,
      {
        method: 'GET',
        headers: {
          'Authorization': `Basic ${Buffer.from(`${this.config.authId}:${this.config.authToken}`).toString('base64')}`,
          'Accept': 'application/json',
        },
        timeout: this.config.decisionTimeout,
      }
    );

    if (!response.ok) {
      throw new Error(`Plivo API error: ${response.status}`);
    }

    const data = await response.json() as PlivoCallResponse;
    return this.mapToCarrierCall(data);
  }

  async blockCall(callSid: string): Promise<void> {
    await this.executeCarrierAction('block', callSid);
  }

  async flagCall(callSid: string): Promise<void> {
    await this.executeCarrierAction('flag', callSid);
  }

  async allowCall(callSid: string): Promise<void> {
    await this.executeCarrierAction('allow', callSid);
  }

  async getSms(messageSid: string): Promise<CarrierSms> {
    const response = await fetch(
      `${this.apiBaseUrl}/v1/Account/${this.config.authId}/Message/${messageSid}/`,
      {
        method: 'GET',
        headers: {
          'Authorization': `Basic ${Buffer.from(`${this.config.authId}:${this.config.authToken}`).toString('base64')}`,
          'Accept': 'application/json',
        },
        timeout: this.config.decisionTimeout,
      }
    );

    if (!response.ok) {
      throw new Error(`Plivo API error: ${response.status}`);
    }

    const data = await response.json() as PlivoSmsResponse;
    return this.mapToCarrierSms(data);
  }

  async blockSms(messageSid: string): Promise<void> {
    await this.executeCarrierAction('block', messageSid, 'sms');
  }

  async flagSms(messageSid: string): Promise<void> {
    await this.executeCarrierAction('flag', messageSid, 'sms');
  }

  async allowSms(messageSid: string): Promise<void> {
    await this.executeCarrierAction('allow', messageSid, 'sms');
  }

  async isHealthy(): Promise<boolean> {
    try {
      const response = await fetch(
        `${this.apiBaseUrl}/v1/Account/${this.config.authId}/`,
        {
          method: 'GET',
          headers: {
            'Authorization': `Basic ${Buffer.from(`${this.config.authId}:${this.config.authToken}`).toString('base64')}`,
          },
          timeout: 5000,
        }
      );
      return response.ok;
    } catch {
      return false;
    }
  }

  private async executeCarrierAction(
    action: 'block' | 'flag' | 'allow',
    sid: string,
    type: 'call' | 'sms' = 'call'
  ): Promise<void> {
    const endpoint = type === 'call'
      ? `${this.apiBaseUrl}/v1/Account/${this.config.authId}/Call/${sid}/`
      : `${this.apiBaseUrl}/v1/Account/${this.config.authId}/Message/${sid}/`;

    // Plivo uses a custom header for action control
    const actionHeader = 
      action === 'block' ? 'spam-block' :
      action === 'flag' ? 'spam-flag' : 'allow';

    await fetch(endpoint, {
      method: 'POST',
      headers: {
        'Authorization': `Basic ${Buffer.from(`${this.config.authId}:${this.config.authToken}`).toString('base64')}`,
        'Content-Type': 'application/json',
        'X-ShieldAI-Action': actionHeader,
      },
      body: JSON.stringify({ action }),
      timeout: this.config.decisionTimeout,
    });
  }

  private mapToCarrierCall(data: PlivoCallResponse): CarrierCall {
    return {
      callSid: data.callUuid || data.resourceUri,
      from: data.from,
      to: data.to,
      status: this.mapCallStatus(data.status),
      startTime: new Date(data.startTime || data.callStartTime),
      duration: data.duration ? parseInt(data.duration) : undefined,
      metadata: {
        plivoPrice: data.price,
        plivoDirection: data.direction,
        plivoAnswerTime: data.answerTime,
      },
    };
  }

  private mapToCarrierSms(data: PlivoSmsResponse): CarrierSms {
    return {
      messageSid: data.messageUuid || data.resourceUri,
      from: data.from,
      to: data.to,
      body: data.text,
      direction: this.mapSmsDirection(data.direction),
      status: this.mapSmsStatus(data.status),
      timestamp: new Date(data.sendTime || data.time),
      metadata: {
        plivoNumParts: data.numParts,
        plivoType: data.type,
        plivoError: data.error,
      },
    };
  }

  private mapCallStatus(status: string): CarrierCall['status'] {
    const statusMap: Record<string, CarrierCall['status']> = {
      'in-progress': 'in-progress',
      'completed': 'completed',
      'failed': 'failed',
      'ringing': 'ringing',
      'busy': 'failed',
      'no-answer': 'failed',
    };
    return statusMap[status] ?? 'failed';
  }

  private mapSmsDirection(direction: string): CarrierSms['direction'] {
    return direction === 'inbound' ? 'inbound' : 'outbound';
  }

  private mapSmsStatus(status: string): CarrierSms['status'] {
    const statusMap: Record<string, CarrierSms['status']> = {
      'queued': 'queued',
      'sent': 'sent',
      'delivered': 'delivered',
      'failed': 'failed',
      'undelivered': 'failed',
    };
    return statusMap[status] ?? 'failed';
  }
}

interface PlivoCallResponse {
  callUuid?: string;
  resourceUri: string;
  from: string;
  to: string;
  status: string;
  startTime?: string;
  callStartTime?: string;
  duration?: string;
  price?: string;
  direction?: string;
  answerTime?: string;
}

interface PlivoSmsResponse {
  messageUuid?: string;
  resourceUri: string;
  from: string;
  to: string;
  text: string;
  direction: string;
  status: string;
  sendTime?: string;
  time?: string;
  numParts?: string;
  type?: string;
  error?: string;
}
