/**
 * Shared utilities for channel adapters.
 */
import type { Attachment, InboundMessage } from './types.js';
import type { PairingManager } from './pairing.js';

/**
 * Split a long message into chunks that respect a platform's character limit.
 * Prefers splitting at newlines, then spaces, then hard-cuts.
 */
export function splitMessage(text: string, maxLength: number): string[] {
  const chunks: string[] = [];
  let remaining = text;

  while (remaining.length > 0) {
    if (remaining.length <= maxLength) {
      chunks.push(remaining);
      break;
    }

    // Try to split at a newline within the allowed window
    let splitIndex = remaining.lastIndexOf('\n', maxLength);
    if (splitIndex === -1 || splitIndex < maxLength / 2) {
      splitIndex = remaining.lastIndexOf(' ', maxLength);
    }
    if (splitIndex === -1 || splitIndex < maxLength / 2) {
      splitIndex = maxLength;
    }

    chunks.push(remaining.slice(0, splitIndex));
    remaining = remaining.slice(splitIndex).trimStart();
  }

  return chunks;
}

/**
 * Normalize reset command variants to a canonical text form.
 * Returns '!reset' for recognized variants, otherwise returns the original text.
 */
export function normalizeResetCommandText(text: string): string {
  if (text === '!reset' || text === 'reset') {
    return '!reset';
  }
  return text;
}

/** Check whether a value is allowed by an optional allowlist (empty/missing list means allow-all). */
export function isAllowedByAllowlist(value: string, allowlist?: string[]): boolean {
  if (!allowlist || allowlist.length === 0) {
    return true;
  }
  return allowlist.includes(value);
}

/**
 * Returns true when mention gating should ignore the message.
 * `requireMention` defaults per adapter; if enabled, the message must mention the bot.
 */
export function shouldIgnoreForMissingMention(params: {
  requireMention: boolean | undefined;
  defaultRequireMention: boolean;
  mentionsBot: boolean;
}): boolean {
  const needsMention = params.requireMention ?? params.defaultRequireMention;
  return needsMention && !params.mentionsBot;
}

interface PairingGateParams {
  pairingManager?: PairingManager;
  channel: InboundMessage['channel'];
  senderId: string;
  text: string;
  /** True for trusted senders/channels that may bypass pairing. */
  isTrusted: boolean;
  /** Optional side effect when a pairing code is accepted. */
  onPaired?: () => Promise<void> | void;
}

/**
 * Shared pairing gate for channel adapters.
 * Returns true when the inbound message should continue to normal processing.
 */
export async function allowTrustedOrPairedSender(params: PairingGateParams): Promise<boolean> {
  const pm = params.pairingManager;
  if (params.isTrusted) {
    return true;
  }
  if (!pm?.enabled) {
    return false;
  }
  if (pm.isApproved(params.channel, params.senderId)) {
    return true;
  }
  const code = params.text.trim();
  if (!code || !pm.validateCode(params.channel, params.senderId, code)) {
    return false;
  }
  await params.onPaired?.();
  return false;
}

interface ResetMessageParams {
  id: string;
  channel: InboundMessage['channel'];
  senderId: string;
  senderName?: string;
  timestamp?: number;
  attachments?: Attachment[];
}

/** Build a normalized inbound reset command message. */
export function buildResetInboundMessage(params: ResetMessageParams): InboundMessage {
  return {
    id: params.id,
    channel: params.channel,
    senderId: params.senderId,
    senderName: params.senderName,
    text: '!reset',
    timestamp: params.timestamp ?? Date.now(),
    metadata: { isCommand: true, command: 'reset' },
    ...(params.attachments && params.attachments.length > 0 ? { attachments: params.attachments } : {}),
  };
}
