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

const FAST2SMS_FIELD_SCHEMA: FieldSchema[] = [
  { key: 'api_key', label: 'API key', type: 'secret', required: true, section: 'Account' },
  {
    key: 'route',
    label: 'Route',
    type: 'select',
    default: 'dlt',
    options: [
      { value: 'dlt', label: 'DLT SMS (template mapped in Fast2SMS)' },
      { value: 'dlt_manual', label: 'DLT manual' },
      { value: 'q', label: 'Quick SMS' },
      { value: 'otp', label: 'OTP' },
    ],
  },
  {
    key: 'sender_id',
    label: 'Sender ID (header)',
    type: 'text',
    required: true,
    validator: 'header',
    dlt: true,
    section: 'India DLT',
  },
  {
    key: 'entity_id',
    label: 'Principal Entity ID',
    type: 'text',
    optional: true,
    validator: 'digits19',
    dlt: true,
    section: 'India DLT',
  },
  {
    key: 'template_id',
    label: 'Default DLT template / message ID',
    type: 'text',
    optional: true,
    dlt: true,
    section: 'India DLT',
  },
];

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

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

  public async validateConfiguration(c: ResolvedCredentials): Promise<void> {
    if (!c.api_key) throw new Error("Fast2SMS requires 'api_key'");
    if (!c.sender_id) throw new Error("Fast2SMS requires 'sender_id'");
    if (c.route === 'dlt_manual' && !c.entity_id) {
      throw new Error('Principal Entity ID is required for the DLT manual route');
    }
  }

  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 route = String(c.route || 'dlt');
    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 ((route === 'dlt' || route === 'dlt_manual') && !templateId) {
      return {
        success: false,
        error: {
          code: 'PROVIDER_REJECTED',
          message: 'Fast2SMS DLT route requires a template ID on the message',
        },
      };
    }

    const ordered =
      (m.metadata?.template_variable_values as string[] | undefined) ||
      Object.values(
        (m.metadata?.template_variables as Record<string, string> | undefined) ||
          m.content.variables ||
          {},
      ).map((v) => String(v));

    const params = new URLSearchParams();
    params.set('route', route);
    params.set('sender_id', String(c.sender_id));
    params.set('message', m.content.body);
    params.set('numbers', m.recipient.replace(/\D/g, ''));
    if (templateId) params.set('template_id', templateId);
    if (ordered.length > 0) params.set('variables', ordered.join('|'));
    if (c.entity_id) params.set('entity_id', String(c.entity_id));

    try {
      const res = await this.fetchFn('https://www.fast2sms.com/dev/bulkV2', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          authorization: String(c.api_key),
        },
        body: params.toString(),
        signal,
      });

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

      if (res.ok && data.return === true) {
        return {
          success: true,
          provider_reference: String(data.request_id || `f2s_${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 || `Fast2SMS 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 || 'Fast2SMS 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,
      };
    }
  }
}
