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

export class TwilioWhatsAppProviderAdapter implements ProviderAdapter {
  public readonly id: string;
  public readonly channel: Channel = 'whatsapp';
  private fetchFn: typeof fetch;

  constructor(options: { id?: string; fetchFn?: typeof fetch } = {}) {
    this.id = options.id || 'twilio-whatsapp';
    this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
  }

  public async validateConfiguration(c: ResolvedCredentials): Promise<void> {
    const sid = c.sid || c.account_sid || c.accountSid;
    const token = c.token || c.auth_token || c.authToken;
    if (!sid || !token) {
      throw new Error("Twilio WhatsApp configuration requires 'sid' and 'token'");
    }
  }

  public async send(
    m: NormalizedMessage,
    c: ResolvedCredentials,
    signal: AbortSignal
  ): Promise<ProviderResult> {
    const sid = String(c.sid || c.account_sid || c.accountSid || '');
    const token = String(c.token || c.auth_token || c.authToken || '');
    let fromNumber = String(c.from || c.fromNumber || '');

    if (!fromNumber.startsWith('whatsapp:')) {
      fromNumber = `whatsapp:${fromNumber}`;
    }

    const toNumber = m.recipient.startsWith('whatsapp:') ? m.recipient : `whatsapp:${m.recipient}`;

    const url = `https://api.twilio.com/2010-04-01/Accounts/${encodeURIComponent(sid)}/Messages.json`;

    const bodyParams = new URLSearchParams();
    bodyParams.set('To', toNumber);
    bodyParams.set('From', fromNumber);
    bodyParams.set('Body', m.content.body);
    const firstAtt = m.content.attachments?.[0];
    if (firstAtt) {
      if (!firstAtt.url) {
        return {
          success: false,
          error: {
            code: 'PROVIDER_REJECTED',
            message: "Twilio WhatsApp media requires 'attachment.url' (public HTTPS MediaUrl)",
          },
        };
      }
      bodyParams.set('MediaUrl', firstAtt.url);
    }

    const authHeader = 'Basic ' + Buffer.from(`${sid}:${token}`).toString('base64');

    try {
      const res = await this.fetchFn(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          Authorization: authHeader,
        },
        body: bodyParams.toString(),
        signal,
      });

      const resData: any = await res.json().catch(() => ({}));

      if (res.status === 201 || res.status === 200) {
        return {
          success: true,
          provider_reference: resData.sid || `tw_wa_${Date.now()}`,
          raw_response: resData,
        };
      }

      let code: ProviderErrorCode = 'PROVIDER_5XX';
      if (res.status === 401 || res.status === 403) code = 'CREDENTIAL_ERROR';
      else if (res.status === 400) code = 'PROVIDER_REJECTED';
      else if (res.status === 429) code = 'PROVIDER_UNAVAILABLE';

      return {
        success: false,
        error: {
          code,
          message: resData.message || `Twilio WhatsApp error status: ${res.status}`,
          raw: resData,
        },
      };
    } catch (err: any) {
      const isTimeout = signal.aborted || err.name === 'AbortError';
      return {
        success: false,
        error: {
          code: isTimeout ? 'TIMEOUT' : 'PROVIDER_5XX',
          message: err.message || 'Twilio WhatsApp dispatch failed',
        },
      };
    }
  }

  public async healthCheck(c: ResolvedCredentials): Promise<ProviderHealth> {
    const startTime = Date.now();
    const sid = String(c.sid || c.account_sid || c.accountSid || '');
    const token = String(c.token || c.auth_token || c.authToken || '');

    if (!sid || !token) {
      return { status: 'down', latency_ms: 0, checked_at: Date.now(), message: 'Missing credentials' };
    }

    const url = `https://api.twilio.com/2010-04-01/Accounts/${encodeURIComponent(sid)}.json`;
    const authHeader = 'Basic ' + Buffer.from(`${sid}:${token}`).toString('base64');

    try {
      const res = await this.fetchFn(url, {
        method: 'GET',
        headers: { Authorization: authHeader },
      });

      const latency = Date.now() - startTime;
      if (res.ok) {
        return { status: 'healthy', latency_ms: latency, checked_at: Date.now() };
      }
      return { status: 'down', latency_ms: latency, checked_at: Date.now(), message: `HTTP ${res.status}` };
    } catch (err: any) {
      return { status: 'down', latency_ms: Date.now() - startTime, checked_at: Date.now(), message: err.message };
    }
  }

  public parseWebhook(req: WebhookRequest): DeliveryEvent | null {
    const body = (req.body as Record<string, string>) || {};
    const messageSid = body.MessageSid || body.SmsSid;
    const messageStatus = body.MessageStatus || body.SmsStatus;

    if (messageSid && messageStatus) {
      const isDelivered = messageStatus.toLowerCase() === 'delivered';
      return {
        message_id: messageSid,
        status: isDelivered ? 'delivered' : 'undelivered',
        timestamp: Date.now(),
        provider: this.id,
        raw: body,
      };
    }

    return null;
  }
}

