import {
  classifyRuntimeDiagnostic,
  selectRuntimeDiagnosticClassification,
} from '../../runtime/RuntimeDiagnosticClassifier';

import type { OpenCodePromptDeliveryLedgerRecord } from './OpenCodePromptDeliveryLedger';

export function normalizeOpenCodeRuntimeDeliveryDiagnostic(
  message: string | null | undefined
): string | null {
  return classifyRuntimeDiagnostic(message).normalizedMessage;
}

export function isGenericOpenCodeRuntimeDeliveryDiagnostic(message: string): boolean {
  return classifyRuntimeDiagnostic(message).generic;
}

export function selectOpenCodeRuntimeDeliveryReason(
  record: OpenCodePromptDeliveryLedgerRecord
): string | null {
  const candidates = [...record.diagnostics.slice().reverse(), record.lastReason].filter(
    (diagnostic) => !isInformationalOpenCodeRuntimeDeliveryDiagnostic(diagnostic)
  );
  const selected = selectRuntimeDiagnosticClassification(candidates);

  if (selected && !selected.generic && selected.normalizedMessage) {
    return boundOpenCodeRuntimeDeliveryReason(selected.normalizedMessage);
  }

  const fallback = getOpenCodeRuntimeDeliveryStateFallback(record);
  if (fallback) {
    return fallback;
  }

  return selected ? 'OpenCode runtime delivery did not complete.' : null;
}

function isInformationalOpenCodeRuntimeDeliveryDiagnostic(
  message: string | null | undefined
): boolean {
  const normalized = message?.trim().toLowerCase();
  return (
    normalized === 'opencode app mcp is connected for message delivery.' ||
    normalized ===
      'opencode prompt_async accepted; response observation will continue through durable app-side ledger reconciliation.' ||
    normalized === 'opencode session status busy' ||
    normalized === 'opencode_delivery_response_pending'
  );
}

export function isActionRequiredOpenCodeRuntimeDeliveryReason(
  message: string | null | undefined
): boolean {
  return classifyRuntimeDiagnostic(message).actionRequired;
}

function getOpenCodeRuntimeDeliveryStateFallback(
  record: OpenCodePromptDeliveryLedgerRecord
): string | null {
  const state = record.responseState?.trim();
  const reason = record.lastReason?.trim();
  const normalizedReason = reason?.toLowerCase();
  const diagnostics = record.diagnostics.map((diagnostic) => diagnostic.trim().toLowerCase());
  if (state === 'empty_assistant_turn' || normalizedReason === 'empty_assistant_turn') {
    return 'OpenCode returned an empty assistant turn.';
  }
  if (
    normalizedReason === 'visible_reply_missing_task_refs' ||
    diagnostics.includes('visible_reply_missing_task_refs') ||
    diagnostics.includes('visible_reply_missing_task_refs_after_merge')
  ) {
    return 'OpenCode created a reply without the required taskRefs metadata.';
  }
  if (diagnostics.includes('visible_reply_task_refs_merge_failed')) {
    return 'OpenCode created a reply without the required taskRefs metadata, and the app could not attach it automatically.';
  }
  if (
    normalizedReason === 'visible_reply_still_required' ||
    normalizedReason === 'visible_reply_ack_only_still_requires_answer' ||
    normalizedReason === 'plain_text_ack_only_still_requires_answer'
  ) {
    return 'OpenCode responded, but did not create a visible message_send reply.';
  }
  if (
    state === 'prompt_delivered_no_assistant_message' ||
    normalizedReason === 'prompt_delivered_no_assistant_message'
  ) {
    return 'OpenCode accepted the prompt, but no assistant turn was recorded.';
  }
  if (
    normalizedReason === 'visible_reply_destination_not_found_yet' ||
    normalizedReason === 'visible_reply_missing_relayofmessageid'
  ) {
    return 'OpenCode created a reply without the required relayOfMessageId correlation.';
  }
  if (normalizedReason === 'non_visible_tool_without_task_progress') {
    return 'OpenCode used tools, but did not create a visible reply or task progress proof.';
  }
  return null;
}

function boundOpenCodeRuntimeDeliveryReason(reason: string): string {
  return reason.length > 500 ? `${reason.slice(0, 497).trimEnd()}...` : reason;
}
