import type { Channel } from '@wa-ticketing/shared';
import type { GroupMetadata, WhatsAppMediaPayload, WhatsAppProviderAdapter } from './whatsappProvider.js';

const DEFAULT_WORKER_URL = process.env.WA_WORKER_URL ?? 'http://whatsapp:3002';

const JSON_HEADERS = { 'Content-Type': 'application/json' };

/** Wraps the Baileys worker HTTP API; URLs, payloads and error messages match the pre-adapter behaviour. */
export class BaileysWhatsAppProvider implements WhatsAppProviderAdapter {
  constructor(
    private readonly fetchImpl: typeof fetch = fetch,
    private readonly workerUrl: string = DEFAULT_WORKER_URL,
  ) {}

  private post(path: string, payload: unknown): Promise<Response> {
    return this.fetchImpl(`${this.workerUrl}${path}`, {
      method: 'POST',
      headers: JSON_HEADERS,
      body: JSON.stringify(payload),
    });
  }

  private async send(channel: Channel, payload: Record<string, unknown>, label: string): Promise<string | null> {
    const res = await this.post('/send', { channelId: channel.id, ...payload });
    if (!res.ok) {
      const body = await res.text().catch(() => '');
      throw new Error(`${label} for ${channel.id}: ${res.status} ${body}`);
    }
    const data = await res.json() as { ok: boolean; waMessageId: string | null };
    return data.waMessageId ?? null;
  }

  sendText(channel: Channel, recipient: string, text: string): Promise<string | null> {
    return this.send(channel, { phone: recipient, text, kind: 'text' }, 'Worker send failed');
  }

  sendMedia(channel: Channel, recipient: string, media: WhatsAppMediaPayload): Promise<string | null> {
    return this.send(channel, {
      phone: recipient,
      kind: media.kind,
      mediaBase64: media.mediaBase64,
      mediaMime: media.mediaMime,
      caption: media.caption,
      fileName: media.fileName,
    }, 'Worker media send failed');
  }

  async startSession(channel: Channel, clearAuth: boolean): Promise<void> {
    const res = await this.post('/sessions', { channelId: channel.id, clearAuth });
    if (!res.ok) {
      const body = await res.text().catch(() => '');
      throw new Error(`Worker rejected session start for ${channel.id}: ${res.status} ${body}`);
    }
  }

  async terminateSession(channel: Channel): Promise<void> {
    await this.fetchImpl(`${this.workerUrl}/sessions/${channel.id}`, { method: 'DELETE' });
  }

  async createGroup(channel: Channel, subject: string, phones: string[]): Promise<{ groupJid: string; subject: string }> {
    const res = await this.post('/group/create', { channelId: channel.id, subject, phones });
    if (!res.ok) {
      const body = await res.text().catch(() => '');
      throw new Error(`Worker group create failed for ${channel.id}: ${res.status} ${body}`);
    }
    const data = await res.json() as { ok: boolean; groupJid: string; subject: string };
    if (!data.groupJid) throw new Error('Worker did not return groupJid');
    return { groupJid: data.groupJid, subject: data.subject ?? subject };
  }

  async getGroupMetadata(channel: Channel, groupJid: string): Promise<GroupMetadata> {
    const res = await this.post('/group/metadata', { channelId: channel.id, groupJid });
    if (!res.ok) {
      const body = await res.text().catch(() => '');
      throw new Error(`Worker group metadata failed: ${res.status} ${body}`);
    }
    return res.json() as Promise<GroupMetadata>;
  }

  async updateGroupParticipants(
    channel: Channel,
    groupJid: string,
    action: 'add' | 'remove',
    phones: string[],
  ): Promise<void> {
    const res = await this.post('/group/participants', { channelId: channel.id, groupJid, action, phones });
    if (!res.ok) {
      const body = await res.text().catch(() => '');
      throw new Error(`Worker group participants ${action} failed: ${res.status} ${body}`);
    }
  }
}
