import { isRecord, nonEmptyString, type UnknownRecord } from '../fiber/narrow.ts';

export type NativeCallName = 'metadata' | 'proof';

export type PageMessage =
  | { kind: 'ready' }
  | { kind: 'closed' }
  | { kind: 'session'; sessionId: number | null }
  | { kind: 'call'; id: string; name: NativeCallName; args: UnknownRecord }
  | { kind: 'log'; level: string; text: string };

const CALL_NAMES: readonly NativeCallName[] = ['metadata', 'proof'];

function isCallName(value: unknown): value is NativeCallName {
  return CALL_NAMES.some((name) => name === value);
}

export function parsePageMessage(raw: string): PageMessage | null {
  let parsed: unknown;
  try {
    parsed = JSON.parse(raw);
  } catch {
    return null;
  }
  if (!isRecord(parsed)) {
    return null;
  }
  switch (parsed.kind) {
    case 'ready':
      return { kind: 'ready' };
    case 'closed':
      return { kind: 'closed' };
    case 'session':
      return typeof parsed.sessionId === 'number' || parsed.sessionId === null ? { kind: 'session', sessionId: parsed.sessionId } : null;
    case 'log':
      return typeof parsed.text === 'string' ? { kind: 'log', level: nonEmptyString(parsed.level) ?? 'log', text: parsed.text } : null;
    case 'call': {
      const id = nonEmptyString(parsed.id);
      return id !== null && isCallName(parsed.name) ? { kind: 'call', id, name: parsed.name, args: isRecord(parsed.args) ? parsed.args : {} } : null;
    }
    default:
      return null;
  }
}

function safeJson(value: unknown): string {
  return JSON.stringify(value ?? null).replace(/</g, '\\u003c').replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
}

export function buildResolveScript(id: string, value: unknown, error: string | null): string {
  return `window.__blarNative && window.__blarNative.resolve(${safeJson(id)}, ${safeJson(value)}, ${safeJson(error)}); true;`;
}

export function buildEventScript(name: string, payload: unknown): string {
  return `window.__blarNative && window.__blarNative.event(${safeJson(name)}, ${safeJson(payload)}); true;`;
}
