import { randomUUID } from 'crypto';
import type { Channel, ChannelStatus, WhatsAppCapabilities, WhatsAppProvider } from '@wa-ticketing/shared';
import { createAfricasTalkingWhatsAppProvider } from './africasTalkingWhatsAppProvider.js';
import { getWhatsAppCapabilities } from './whatsappCapabilities.js';

export type ChannelCreatePlan = {
  provider: WhatsAppProvider;
  status: ChannelStatus;
  inboundToken: string | null;
};

/** Omitted provider stays Baileys so existing clients of POST /channels/init keep working. */
export function resolveCreateProvider(input: unknown): WhatsAppProvider {
  if (input === undefined || input === null) return 'baileys';
  if (input === 'baileys' || input === 'africas_talking') return input;
  throw Object.assign(new Error('provider must be baileys or africas_talking'), { statusCode: 400 });
}

/**
 * Baileys pairs via QR later; Africa's Talking has no session to pair, so it is connected on
 * creation and reached through an inbound webhook token. Its API secrets come from the deployment
 * env only (validated here) and are never part of the plan.
 */
export function planChannelCreate(
  provider: WhatsAppProvider,
  env: NodeJS.ProcessEnv = process.env,
  newToken: () => string = randomUUID,
): ChannelCreatePlan {
  if (provider === 'baileys') return { provider, status: 'connecting', inboundToken: null };
  createAfricasTalkingWhatsAppProvider(env);
  return { provider, status: 'connected', inboundToken: newToken() };
}

/** Throws a typed 400 unless the channel's provider supports the capability. */
export function assertCapability(channel: Channel, capability: keyof WhatsAppCapabilities): void {
  const supported = channel.kind === 'whatsapp' && channel.provider
    ? getWhatsAppCapabilities(channel.provider)[capability]
    : false;
  if (supported) return;
  throw Object.assign(
    new Error(`This channel's WhatsApp provider does not support ${capability}`),
    { statusCode: 400, code: 'unsupported_capability', capability },
  );
}

/**
 * Whether a staff outbound may reach the provider adapter.
 *
 * Connected channels always may. The exception is an Africa's Talking WhatsApp channel sitting at
 * `disconnected`: that status is only ever set by an outbound authentication failure, and the
 * provider has no session and no reconnect, so there is nothing an operator can press to clear it.
 * Refusing sends would strand the channel permanently. Letting the send through makes it the
 * revalidation path — `trackOutboundProviderStatus` restores `connected` when the corrected
 * credentials work, and leaves it disconnected (error propagating) when they still do not.
 *
 * Baileys is untouched: its `disconnected` means a genuinely dead linked-device session that a send
 * cannot revive, and the worker owns that status.
 */
export function canStaffSendOnChannel<T extends Pick<Channel, 'kind' | 'provider' | 'status'>>(
  channel: T | null | undefined,
): channel is T {
  if (!channel) return false;
  if (channel.status === 'connected') return true;
  return channel.kind === 'whatsapp'
    && channel.provider === 'africas_talking'
    && channel.status === 'disconnected';
}

/** Group JID when the ticket is group-backed (needs `groups`), else the contact's phone. */
export function resolveWhatsAppTarget(channel: Channel, groupJid: string | null | undefined, contactPhone: string): string {
  const group = groupJid?.trim();
  if (!group) return contactPhone;
  assertCapability(channel, 'groups');
  return group;
}

export function isUnsupportedCapabilityError(err: unknown): boolean {
  return typeof err === 'object' && err !== null && (err as { code?: unknown }).code === 'unsupported_capability';
}

/** The inbound token is the sole public webhook credential: only admins may read it. */
export function redactInboundToken(channel: Channel, role: string | undefined): Channel {
  return role === 'admin' ? channel : { ...channel, inbound_token: undefined };
}

/**
 * Channel JSON embedded in ticket payloads (raw `row_to_json`): adds API-computed capabilities for
 * WhatsApp channels and never carries the webhook token, which only admin channel endpoints expose.
 */
export function serializeEmbeddedChannel<T extends Channel | null | undefined>(channel: T): T {
  if (!channel) return channel;
  const { inbound_token: _token, ...rest } = channel;
  const capabilities = rest.kind === 'whatsapp' && rest.provider ? getWhatsAppCapabilities(rest.provider) : undefined;
  return { ...rest, ...(capabilities && { capabilities }) } as T;
}
