/**
 * Outbox dispatcher: maps a claimed OutboxEntry to the right channel impl.
 *
 * Defined as a function type so the service stays decoupled from the channel
 * implementations — tests inject a stub, production wires the real channels.
 *
 * The payload carries both the formatted message and a snapshot of the
 * channel config at enqueue time. Snapshotting deliberately decouples the
 * outbox from the channel-config storage layer (encryption, schema, soft
 * delete) and from race conditions where a channel is edited mid-flight.
 */

import { WebhookChannel } from '../channels/webhook.channel';
import { TelegramChannel } from '../channels/telegram.channel';
import { PagerDutyChannel } from '../channels/pagerduty.channel';
import { TeamsChannel } from '../channels/teams.channel';
import type { ChannelConfig } from '../types';
import type { DeliveryResult, OutboxEntry } from './types';

export type OutboxDeliveryFn = (entry: OutboxEntry) => Promise<DeliveryResult>;

export interface OutboxPayload {
    message: string;
    config: ChannelConfig;
}

export function encodeOutboxPayload(payload: OutboxPayload): string {
    return JSON.stringify(payload);
}

function parsePayload(raw: string): OutboxPayload | null {
    try {
        const parsed = JSON.parse(raw);
        if (typeof parsed?.message !== 'string') return null;
        if (typeof parsed?.config !== 'object' || parsed.config === null) return null;
        return parsed as OutboxPayload;
    } catch {
        return null;
    }
}

export const defaultDelivery: OutboxDeliveryFn = async (entry) => {
    const payload = parsePayload(entry.payload);
    if (!payload) return { ok: false, error: 'Invalid outbox payload' };

    try {
        if (entry.channelType === 'webhook') {
            await new WebhookChannel().sendOrThrow(payload.message, payload.config);
            return { ok: true };
        }
        if (entry.channelType === 'telegram') {
            await new TelegramChannel().sendOrThrow(payload.message, payload.config);
            return { ok: true };
        }
        if (entry.channelType === 'pagerduty') {
            await new PagerDutyChannel().sendOrThrow(payload.message, payload.config);
            return { ok: true };
        }
        if (entry.channelType === 'teams') {
            await new TeamsChannel().sendOrThrow(payload.message, payload.config);
            return { ok: true };
        }
        return { ok: false, error: `Unsupported channel type: ${entry.channelType}` };
    } catch (err) {
        return { ok: false, error: err instanceof Error ? err.message : String(err) };
    }
};
