/**
 * Shared formatting functions for MCP traffic events.
 *
 * Extracted from inspect-app.tsx so they can be reused by
 * the unified timeline, action area, and provenance views.
 */

import type { TrafficEventType } from './unified-types.js';

/** Safely dig into unknown objects */
export function dig(obj: unknown, ...keys: string[]): unknown {
  let cur = obj;
  for (const k of keys) {
    if (cur === null || cur === undefined || typeof cur !== 'object') return undefined;
    cur = (cur as Record<string, unknown>)[k];
  }
  return cur;
}

export function trunc(s: string, maxLen: number): string {
  return s.length > maxLen ? s.slice(0, maxLen - 1) + '\u2026' : s;
}

export function nameList(items: unknown[], key: string, max: number): string {
  if (items.length === 0) return '(none)';
  const names = items.map((it) => dig(it, key) as string).filter(Boolean);
  const shown = names.slice(0, max);
  const rest = names.length - shown.length;
  return shown.join(', ') + (rest > 0 ? ` +${rest} more` : '');
}

export function formatTime(ts: Date | string): string {
  try {
    const d = typeof ts === 'string' ? new Date(ts) : ts;
    return d.toLocaleTimeString('en-GB', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
  } catch {
    return '??:??:??';
  }
}

/** Extract meaningful summary from request params (strips jsonrpc/id boilerplate) */
export function summarizeRequest(method: string, body: unknown): string {
  const params = dig(body, 'params') as Record<string, unknown> | undefined;

  switch (method) {
    case 'initialize': {
      const name = dig(params, 'clientInfo', 'name') ?? '?';
      const ver = dig(params, 'clientInfo', 'version') ?? '';
      const proto = dig(params, 'protocolVersion') ?? '';
      return `client=${name}${ver ? ` v${ver}` : ''} proto=${proto}`;
    }
    case 'tools/call': {
      const toolName = dig(params, 'name') as string ?? '?';
      const args = dig(params, 'arguments') as Record<string, unknown> | undefined;
      if (!args || Object.keys(args).length === 0) return `${toolName}()`;
      const pairs = Object.entries(args).map(([k, v]) => {
        const vs = typeof v === 'string' ? v : JSON.stringify(v);
        return `${k}: ${trunc(vs, 40)}`;
      });
      return `${toolName}(${trunc(pairs.join(', '), 80)})`;
    }
    case 'resources/read': {
      const uri = dig(params, 'uri') as string ?? '';
      return uri;
    }
    case 'prompts/get': {
      const name = dig(params, 'name') as string ?? '';
      return name;
    }
    case 'tools/list':
    case 'resources/list':
    case 'prompts/list':
    case 'notifications/initialized':
      return '';
    default: {
      if (!params || Object.keys(params).length === 0) return '';
      const s = JSON.stringify(params);
      return trunc(s, 80);
    }
  }
}

/** Extract meaningful summary from response result */
export function summarizeResponse(method: string, body: unknown, durationMs?: number): string {
  const error = dig(body, 'error') as { message?: string; code?: number } | undefined;
  if (error) {
    return `ERROR ${error.code ?? ''}: ${error.message ?? 'unknown'}`;
  }

  const result = dig(body, 'result') as Record<string, unknown> | undefined;
  if (!result) return '';

  let summary: string;
  switch (method) {
    case 'initialize': {
      const name = dig(result, 'serverInfo', 'name') ?? '?';
      const ver = dig(result, 'serverInfo', 'version') ?? '';
      const caps = dig(result, 'capabilities') as Record<string, unknown> | undefined;
      const capList = caps ? Object.keys(caps).filter((k) => caps[k] && Object.keys(caps[k] as object).length > 0) : [];
      summary = `server=${name}${ver ? ` v${ver}` : ''}${capList.length ? ` caps=[${capList.join(',')}]` : ''}`;
      break;
    }
    case 'tools/list': {
      const tools = (result.tools ?? []) as unknown[];
      summary = `${tools.length} tools: ${nameList(tools, 'name', 6)}`;
      break;
    }
    case 'resources/list': {
      const resources = (result.resources ?? []) as unknown[];
      summary = `${resources.length} resources: ${nameList(resources, 'name', 6)}`;
      break;
    }
    case 'prompts/list': {
      const prompts = (result.prompts ?? []) as unknown[];
      if (prompts.length === 0) { summary = '0 prompts'; break; }
      summary = `${prompts.length} prompts: ${nameList(prompts, 'name', 6)}`;
      break;
    }
    case 'tools/call': {
      const content = (result.content ?? []) as unknown[];
      const isError = result.isError;
      const first = content[0];
      const text = (dig(first, 'text') as string) ?? '';
      const prefix = isError ? 'ERROR: ' : '';
      if (text) { summary = prefix + trunc(text.replace(/\n/g, ' '), 100); break; }
      summary = prefix + `${content.length} content block(s)`;
      break;
    }
    case 'resources/read': {
      const contents = (result.contents ?? []) as unknown[];
      const first = contents[0];
      const text = (dig(first, 'text') as string) ?? '';
      if (text) { summary = trunc(text.replace(/\n/g, ' '), 80); break; }
      summary = `${contents.length} content block(s)`;
      break;
    }
    case 'notifications/initialized':
      summary = 'ok';
      break;
    default: {
      if (Object.keys(result).length === 0) { summary = 'ok'; break; }
      const s = JSON.stringify(result);
      summary = trunc(s, 80);
      break;
    }
  }

  if (durationMs !== undefined) {
    return `[${durationMs}ms] ${summary}`;
  }
  return summary;
}

/** Format full event body for expanded detail view (multi-line, readable) */
export function formatBodyDetail(eventType: string, method: string, body: unknown): string[] {
  const bodyObj = body as Record<string, unknown> | null;
  if (!bodyObj) return ['(no body)'];

  const lines: string[] = [];

  if (eventType.includes('request') || eventType === 'client_notification') {
    const params = bodyObj['params'] as Record<string, unknown> | undefined;
    if (method === 'tools/call' && params) {
      lines.push(`Tool: ${params['name'] as string}`);
      const args = params['arguments'] as Record<string, unknown> | undefined;
      if (args && Object.keys(args).length > 0) {
        lines.push('Arguments:');
        for (const [k, v] of Object.entries(args)) {
          const vs = typeof v === 'string' ? v : JSON.stringify(v, null, 2);
          for (const vl of vs.split('\n')) {
            lines.push(`  ${k}: ${vl}`);
          }
        }
      }
    } else if (method === 'initialize' && params) {
      const ci = params['clientInfo'] as Record<string, unknown> | undefined;
      lines.push(`Client: ${ci?.['name'] ?? '?'} v${ci?.['version'] ?? '?'}`);
      lines.push(`Protocol: ${params['protocolVersion'] ?? '?'}`);
      const caps = params['capabilities'] as Record<string, unknown> | undefined;
      if (caps) lines.push(`Capabilities: ${JSON.stringify(caps)}`);
    } else if (params && Object.keys(params).length > 0) {
      for (const l of JSON.stringify(params, null, 2).split('\n')) {
        lines.push(l);
      }
    } else {
      lines.push('(empty params)');
    }
  } else if (eventType.includes('response')) {
    const error = bodyObj['error'] as Record<string, unknown> | undefined;
    if (error) {
      lines.push(`Error ${error['code']}: ${error['message']}`);
      if (error['data']) {
        for (const l of JSON.stringify(error['data'], null, 2).split('\n')) {
          lines.push(`  ${l}`);
        }
      }
    } else {
      const result = bodyObj['result'] as Record<string, unknown> | undefined;
      if (!result) {
        lines.push('(empty result)');
      } else if (method === 'tools/list') {
        const tools = (result['tools'] ?? []) as Array<{ name: string; description?: string }>;
        lines.push(`${tools.length} tools:`);
        for (const t of tools) {
          lines.push(`  ${t.name}${t.description ? ` \u2014 ${trunc(t.description, 60)}` : ''}`);
        }
      } else if (method === 'resources/list') {
        const resources = (result['resources'] ?? []) as Array<{ name: string; uri?: string; description?: string }>;
        lines.push(`${resources.length} resources:`);
        for (const r of resources) {
          lines.push(`  ${r.name}${r.uri ? ` (${r.uri})` : ''}${r.description ? ` \u2014 ${trunc(r.description, 50)}` : ''}`);
        }
      } else if (method === 'prompts/list') {
        const prompts = (result['prompts'] ?? []) as Array<{ name: string; description?: string }>;
        lines.push(`${prompts.length} prompts:`);
        for (const p of prompts) {
          lines.push(`  ${p.name}${p.description ? ` \u2014 ${trunc(p.description, 60)}` : ''}`);
        }
      } else if (method === 'tools/call') {
        const isErr = result['isError'];
        const content = (result['content'] ?? []) as Array<{ type?: string; text?: string }>;
        if (isErr) lines.push('(error response)');
        for (const c of content) {
          if (c.text) {
            for (const l of c.text.split('\n')) {
              lines.push(l);
            }
          } else {
            lines.push(`[${c.type ?? 'unknown'} content]`);
          }
        }
      } else if (method === 'initialize') {
        const si = result['serverInfo'] as Record<string, unknown> | undefined;
        lines.push(`Server: ${si?.['name'] ?? '?'} v${si?.['version'] ?? '?'}`);
        lines.push(`Protocol: ${result['protocolVersion'] ?? '?'}`);
        const caps = result['capabilities'] as Record<string, unknown> | undefined;
        if (caps) {
          lines.push('Capabilities:');
          for (const [k, v] of Object.entries(caps)) {
            if (v && typeof v === 'object' && Object.keys(v).length > 0) {
              lines.push(`  ${k}: ${JSON.stringify(v)}`);
            }
          }
        }
        const instructions = result['instructions'] as string | undefined;
        if (instructions) {
          lines.push('');
          lines.push('Instructions:');
          for (const l of instructions.split('\n')) {
            lines.push(`  ${l}`);
          }
        }
      } else {
        for (const l of JSON.stringify(result, null, 2).split('\n')) {
          lines.push(l);
        }
      }
    }
  } else {
    // Lifecycle events
    for (const l of JSON.stringify(bodyObj, null, 2).split('\n')) {
      lines.push(l);
    }
  }

  return lines;
}

export interface FormattedEvent {
  arrow: string;
  color: string;
  label: string;
  detail: string;
  detailColor?: string | undefined;
}

export function formatEventSummary(
  eventType: TrafficEventType,
  method: string | undefined,
  body: unknown,
  upstreamName?: string,
  durationMs?: number,
): FormattedEvent {
  const m = method ?? '';

  switch (eventType) {
    case 'client_request':
      return { arrow: '\u2192', color: 'green', label: m, detail: summarizeRequest(m, body) };
    case 'client_response': {
      const detail = summarizeResponse(m, body, durationMs);
      const hasError = detail.startsWith('ERROR');
      return { arrow: '\u2190', color: 'blue', label: m, detail, detailColor: hasError ? 'red' : undefined };
    }
    case 'client_notification':
      return { arrow: '\u25C2', color: 'magenta', label: m, detail: summarizeRequest(m, body) };
    case 'upstream_request':
      return { arrow: '  \u21E2', color: 'yellowBright', label: `${upstreamName ?? '?'}/${m}`, detail: summarizeRequest(m, body) };
    case 'upstream_response': {
      const detail = summarizeResponse(m, body, durationMs);
      const hasError = detail.startsWith('ERROR');
      return { arrow: '  \u21E0', color: 'yellowBright', label: `${upstreamName ?? '?'}/${m}`, detail, detailColor: hasError ? 'red' : undefined };
    }
    case 'session_created':
      return { arrow: '\u25CF', color: 'cyan', label: 'session', detail: '' };
    case 'session_closed':
      return { arrow: '\u25CB', color: 'red', label: 'session', detail: 'closed' };
    default:
      return { arrow: '?', color: 'white', label: eventType, detail: '' };
  }
}
