import { createHash, randomBytes } from 'node:crypto';

const discoverySalt = randomBytes(16);

interface Message {
  role: string;
  content?: string | null;
  tool_call_id?: string;
  tool_calls?: Array<{ id: string; function: { name: string; arguments: string } }>;
}

/** Keep only fields useful for debugging; tool arguments can contain credentials. */
export function safeTrace(messages: Message[]): Array<Record<string, unknown>> {
  return messages.filter((message) => message.role !== 'user').map((message) => {
    if (message.role === 'assistant') {
      return {
        role: 'assistant',
        calls: message.tool_calls?.map((call) => {
          let args: Record<string, unknown> = {};
          try {
            const parsed: unknown = JSON.parse(call.function.arguments);
            if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) args = parsed as Record<string, unknown>;
          } catch { /* omit malformed arguments */ }
          const params = args.parameters && typeof args.parameters === 'object' ? args.parameters as Record<string, unknown> : {};
          const discoveryKey = call.function.name === 'blu-list-abilities'
            ? createHash('sha256').update(discoverySalt).update(JSON.stringify({ search: args.search ?? '', name_prefix: args.name_prefix ?? '' })).digest('hex').slice(0, 12)
            : undefined;
          return {
            name: call.function.name,
            discoveryKey,
            ability: typeof args.ability_name === 'string' ? args.ability_name : undefined,
            parameterTypes: call.function.name === 'blu-call-ability'
              ? Object.fromEntries(Object.entries(params).map(([key, value]) => [key, Array.isArray(value) ? 'array' : value === null ? 'null' : typeof value]))
              : undefined,
            route: typeof params.route === 'string' ? params.route : undefined,
            method: typeof params.method === 'string' ? params.method : undefined,
          };
        }),
        reply: typeof message.content === 'string' ? message.content.slice(0, 500).replace(/(password\s*[:=]\s*)\S+/gi, '$1[redacted]') : undefined,
      };
    }
    let statusCode: number | undefined;
    let errorCode: string | undefined;
    let invalidParams: string[] | undefined;
    const toolError = typeof message.content === 'string' && message.content.startsWith('MCP error:');
    if (typeof message.content === 'string') {
      try {
        const payload = JSON.parse(message.content) as Record<string, unknown>;
        if (typeof payload.statusCode === 'number') statusCode = payload.statusCode;
        if (statusCode && statusCode >= 400 && payload.message && typeof payload.message === 'object') {
          const detail = payload.message as Record<string, unknown>;
          if (typeof detail.code === 'string' && /^[a-z][a-z0-9_]{1,80}$/.test(detail.code)) errorCode = detail.code;
          const data = detail.data && typeof detail.data === 'object' ? detail.data as Record<string, unknown> : {};
          const params = data.params && typeof data.params === 'object' ? data.params as Record<string, unknown> : {};
          invalidParams = Object.keys(params).filter((key) => /^[a-z][a-z0-9_]{0,80}$/.test(key)).slice(0, 10);
        }
      } catch { /* do not include raw tool output */ }
    }
    return { role: 'tool', statusCode, errorCode, invalidParams, toolError };
  });
}

export function summarizeFailure(messages: Message[]): string {
  const trace = safeTrace(messages);
  if (trace.some((entry) => entry.role === 'tool' && (entry.toolError || typeof entry.statusCode === 'number' && entry.statusCode >= 400))) {
    return 'Tool error';
  }
  const discoveryCounts = new Map<string, number>();
  for (const entry of trace) {
    if (!Array.isArray(entry.calls)) continue;
    for (const call of entry.calls as Array<{ discoveryKey?: string }>) {
      if (call.discoveryKey) discoveryCounts.set(call.discoveryKey, (discoveryCounts.get(call.discoveryKey) ?? 0) + 1);
    }
  }
  if ([...discoveryCounts.values()].some((count) => count >= 3)) return 'Repeated discovery query';
  const calls = trace.flatMap((entry) => Array.isArray(entry.calls) ? entry.calls as Array<{ name: string }> : []);
  if (calls.length === 0) {
    const reply = [...messages].reverse().find((message) => message.role === 'assistant')?.content;
    return typeof reply === 'string' && /\?|\b(?:need|provide|which|what)\b/i.test(reply) ? 'Asked for input' : 'No tool call';
  }
  if (calls.every((call) => ['blu-list-abilities', 'blu-get-ability-schema'].includes(call.name))) return 'Discovery only';
  return 'Different ability';
}
