import { createProducedWorkItem } from './db.js';
import type { WorkItem } from './db/work-items.js';
import { resolveRuntimeAttachmentBaseDirs } from './attachment-base-dirs.js';
import { deliverOpenWorkItem } from './message-runtime-delivery.js';
import { parseVisibleVerdict } from './paired-verdict.js';
import { resolveChannelForDeliveryRole } from './router.js';
import { normalizePairedRoomRole } from './types.js';
import type {
  Channel,
  OutboundAttachment,
  PairedRoomRole,
  RegisteredGroup,
} from './types.js';
import { logger } from './logger.js';

type IpcDeliveryLog = Pick<typeof logger, 'info' | 'warn' | 'error'>;

interface IpcOutboundQueue {
  hasRecordedDirectTerminalDeliveryForRun?(
    groupJid: string,
    runId: string,
    senderRole?: string | null,
  ): boolean;
  noteDirectTerminalDelivery?(
    groupJid: string,
    senderRole?: string | null,
    text?: string | null,
  ): void;
}

export interface IpcOutboundDeliveryArgs {
  jid: string;
  text: string;
  senderRole?: string;
  runId?: string;
  attachments?: OutboundAttachment[];
}

export interface IpcOutboundDeliveryDeps {
  channels: Channel[];
  roomBindings: () => Record<string, RegisteredGroup>;
  queue: IpcOutboundQueue;
  log?: IpcDeliveryLog;
  createWorkItem?: typeof createProducedWorkItem;
  deliverWorkItem?: typeof deliverOpenWorkItem;
}

export interface CanonicalOutboundDeliveryArgs {
  jid: string;
  text: string;
  deliveryRole?: PairedRoomRole;
  attachments?: OutboundAttachment[];
}

export interface CanonicalOutboundDeliveryDeps {
  channels: Channel[];
  roomBindings: () => Record<string, RegisteredGroup>;
  log?: IpcDeliveryLog;
  createWorkItem?: typeof createProducedWorkItem;
  deliverWorkItem?: typeof deliverOpenWorkItem;
}

export type CanonicalOutboundDeliveryResult = 'delivered' | 'queued_retry';

export type IpcOutboundDeliveryResult =
  | 'delivered'
  | 'queued_retry'
  | 'skipped_recorded_terminal';

function isTerminalStatusMessage(text: string): boolean {
  return parseVisibleVerdict(text) !== 'continue';
}

export async function deliverCanonicalOutboundMessage(
  args: CanonicalOutboundDeliveryArgs,
  deps: CanonicalOutboundDeliveryDeps,
): Promise<CanonicalOutboundDeliveryResult> {
  const log = deps.log ?? logger;
  const group = deps.roomBindings()[args.jid];
  if (!group) {
    throw new Error(`No registered room binding for outbound JID: ${args.jid}`);
  }

  const route = resolveChannelForDeliveryRole(
    deps.channels,
    args.jid,
    args.deliveryRole,
  );
  if (!route.channel) throw new Error(`No channel for JID: ${args.jid}`);

  log.info(
    {
      transition: 'outbound:route',
      chatJid: args.jid,
      deliveryRole: args.deliveryRole ?? null,
      requestedRoleChannel: route.requestedRoleChannelName,
      selectedChannel: route.selectedChannelName,
      usedRoleChannel: route.usedRoleChannel,
      fallbackUsed: route.fallbackUsed,
    },
    'Routed outbound message to canonical work item delivery',
  );

  const createWorkItem = deps.createWorkItem ?? createProducedWorkItem;
  const workItem = createWorkItem({
    group_folder: group.folder,
    chat_jid: args.jid,
    agent_type: group.agentType ?? 'claude-code',
    delivery_role: args.deliveryRole ?? null,
    start_seq: null,
    end_seq: null,
    result_payload: args.text,
    attachments: args.attachments,
  });

  const deliverWorkItem = deps.deliverWorkItem ?? deliverOpenWorkItem;
  const delivered = await deliverWorkItem({
    channel: route.channel,
    item: workItem as WorkItem,
    log,
    attachmentBaseDirs: resolveRuntimeAttachmentBaseDirs(group),
    isDuplicateOfLastBotFinal: () => false,
    openContinuation: () => {},
  });

  return delivered ? 'delivered' : 'queued_retry';
}

export async function deliverIpcOutboundMessage(
  args: IpcOutboundDeliveryArgs,
  deps: IpcOutboundDeliveryDeps,
): Promise<IpcOutboundDeliveryResult> {
  const log = deps.log ?? logger;
  const deliveryRole = normalizePairedRoomRole(args.senderRole);
  if (
    args.runId &&
    (deliveryRole === 'reviewer' || deliveryRole === 'arbiter') &&
    deps.queue.hasRecordedDirectTerminalDeliveryForRun?.(
      args.jid,
      args.runId,
      deliveryRole,
    )
  ) {
    log.info(
      {
        transition: 'ipc:skip-post-terminal',
        chatJid: args.jid,
        senderRole: deliveryRole,
        runId: args.runId,
      },
      'Skipped IPC relay message because the run already emitted a direct terminal verdict',
    );
    return 'skipped_recorded_terminal';
  }

  log.info(
    {
      transition: 'ipc:route',
      chatJid: args.jid,
      runId: args.runId ?? null,
      senderRole: deliveryRole ?? null,
    },
    'IPC relay routed message to canonical work item delivery',
  );

  const result = await deliverCanonicalOutboundMessage(
    {
      jid: args.jid,
      text: args.text,
      deliveryRole,
      attachments: args.attachments,
    },
    {
      channels: deps.channels,
      roomBindings: deps.roomBindings,
      log,
      createWorkItem: deps.createWorkItem,
      deliverWorkItem: deps.deliverWorkItem,
    },
  );

  if (result !== 'delivered') {
    return result;
  }

  if (
    (deliveryRole === 'reviewer' || deliveryRole === 'arbiter') &&
    isTerminalStatusMessage(args.text)
  ) {
    deps.queue.noteDirectTerminalDelivery?.(args.jid, deliveryRole, args.text);
  }
  return 'delivered';
}
