/**
 * RPC singleton — breaks the circular import chain.
 *
 * main.ts creates the Electroview and RPC, then sets it here.
 * All other modules import from this file instead of main.ts.
 *
 * Fix #17: Typed RPC interface instead of `any`.
 */

import type { PtyRPCSchema, PtyRPCRequests, PtyRPCMessages } from '../shared/pty-rpc-schema.ts';

// ── Typed RPC interface ──────────────────────────────────────────────────────

type RequestFn<K extends keyof PtyRPCRequests> = (params: PtyRPCRequests[K]['params']) => Promise<PtyRPCRequests[K]['response']>;

type MessagePayload<K extends keyof PtyRPCMessages> = PtyRPCMessages[K];
type MessageListener<K extends keyof PtyRPCMessages> = (payload: MessagePayload<K>) => void;

export interface AppRpcHandle {
  request: { [K in keyof PtyRPCRequests]: RequestFn<K> };
  addMessageListener: <K extends keyof PtyRPCMessages>(event: K, handler: MessageListener<K>) => void;
  removeMessageListener?: <K extends keyof PtyRPCMessages>(event: K, handler: MessageListener<K>) => void;
}

// ── Internal holder ──────────────────────────────────────────────────────────

let _rpc: AppRpcHandle | null = null;

/** Called once from main.ts after Electroview.defineRPC(). */
export function setAppRpc(rpc: AppRpcHandle): void {
  _rpc = rpc;
}

/**
 * The app-wide RPC handle.
 * Safe to call after main.ts has executed (Svelte components mount after).
 */
export const appRpc: AppRpcHandle = new Proxy({} as AppRpcHandle, {
  get(_target, prop) {
    if (!_rpc) {
      // Graceful degradation: return no-ops instead of throwing.
      // This allows the app to mount even when RPC isn't available
      // (e.g., E2E tests loading via http:// instead of views://).
      if (prop === 'request') return new Proxy({}, { get: () => async () => null });
      if (prop === 'addMessageListener') return () => {};
      if (prop === 'removeMessageListener') return () => {};
      console.warn(`[rpc] accessed before init — property "${String(prop)}" (returning no-op)`);
      return () => {};
    }
    return (_rpc as Record<string | symbol, unknown>)[prop];
  },
});
