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

const MSG91_FIELD_SCHEMA: FieldSchema[] = [
  { key: 'auth_key', label: 'Auth key', type: 'secret', required: true, section: 'Account' },
  {
    key: 'route',
    label: 'Route',
    type: 'select',
    default: '4',
    options: [
      { value: '4', label: 'Transactional (route 4)' },
      { value: '1', label: 'Promotional (route 1)' },
      { value: 'otp', label: 'OTP' },
    ],
  },
  {
    key: 'sender_id',
    label: 'Sender ID (header)',
    type: 'text',
    required: true,
    validator: 'header',
    dlt: true,
    section: 'India DLT',
  },
  {
    key: 'pe_id',
    label: 'Principal Entity ID',
    type: 'text',
    required: true,
    validator: 'digits19',
    dlt: true,
    section: 'India DLT',
  },
  {
    key: 'template_id',
    label: 'Default MSG91 template ID',
    type: 'text',
    optional: true,
    dlt: true,
    section: 'India DLT',
  },
  { key: 'country', label: 'Default country code', type: 'text', default: '91', validator: 'num' },
];

export class Msg91SmsProviderAdapter implements ProviderAdapter {
  public readonly id: string;
  public readonly channel: Channel = 'sms';
  public readonly fieldSchema = MSG91_FIELD_SCHEMA;
  private fetchFn: typeof fetch;

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

  public async validateConfiguration(c: ResolvedCredentials): Promise<void> {
    if (!c.auth_key) throw new Error("MSG91 requires 'auth_key'");
    if (!c.sender_id) throw new Error("MSG91 requires DLT 'sender_id'");
    if (!c.pe_id) throw new Error("MSG91 requires DLT 'pe_id'");
  }

  public async send(
    m: NormalizedMessage,
    c: ResolvedCredentials,
    signal: AbortSignal,
  ): Promise<ProviderResult> {
    try {
      await this.validateConfiguration(c);
    } catch (err: any) {
      return { success: false, error: { code: 'CREDENTIAL_ERROR', message: err.message } };
    }

    const authKey = String(c.auth_key);
    const route = String(c.route || '4');
    const sender = String(c.sender_id);
    const peId = String(c.pe_id);
    const contentTemplate = m.content.template ? String(m.content.template) : '';
    const looksLikeDltId = /^\d{10,}$/.test(contentTemplate);
    const templateId =
      (m.metadata?.dlt_template_id as string | undefined) ||
      (looksLikeDltId ? contentTemplate : '') ||
      (c.template_id ? String(c.template_id) : '');

    if (!templateId) {
      return {
        success: false,
        error: {
          code: 'PROVIDER_REJECTED',
          message: 'MSG91 DLT SMS requires a template ID on the message or provider config',
        },
      };
    }

    const mobile = m.recipient.replace(/\D/g, '');
    const country = String(c.country || '91');
    const varSource =
      (m.metadata?.template_variables as Record<string, string> | undefined) ||
      m.content.variables ||
      {};
    const recipient: Record<string, string> = {
      mobiles: mobile.startsWith(country) ? mobile : `${country}${mobile}`,
    };
    for (const [key, value] of Object.entries(varSource)) {
      recipient[key] = String(value);
    }

    const payload = {
      template_id: templateId,
      short_url: '0',
      recipients: [recipient],
      sender: sender,
      PE_ID: peId,
      message: m.content.body,
    };

    try {
      const res = await this.fetchFn('https://control.msg91.com/api/v5/flow/', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          authkey: authKey,
        },
        body: JSON.stringify({ ...payload, route }),
        signal,
      });

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

      if (res.ok && (data.type === 'success' || data.message)) {
        return {
          success: true,
          provider_reference: String(data.request_id || data.message || `msg91_${Date.now()}`),
          raw_response: data,
        };
      }

      let code: ProviderErrorCode = 'PROVIDER_5XX';
      if (res.status === 401 || res.status === 403) code = 'CREDENTIAL_ERROR';
      else if (res.status >= 400 && res.status < 500) code = 'PROVIDER_REJECTED';

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

  public async healthCheck(c: ResolvedCredentials): Promise<ProviderHealth> {
    const start = Date.now();
    try {
      await this.validateConfiguration(c);
      return { status: 'healthy', latency_ms: Date.now() - start, checked_at: Date.now() };
    } catch (err: any) {
      return {
        status: 'down',
        latency_ms: Date.now() - start,
        checked_at: Date.now(),
        message: err.message,
      };
    }
  }

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