/**
 * Custom electron IPC ↔ tRPC v11 bridge.
 *
 * Replaces the incompatible electron-trpc package (v0.7.x bundles tRPC v10's
 * callProcedure which checks `procedure._def[type]`, while tRPC v11 uses
 * `procedure._def.type`).
 */
import { ipcMain, type BrowserWindow } from 'electron';
import {
  callTRPCProcedure,
  getErrorShape,
  getTRPCErrorFromUnknown,
  transformTRPCResponse,
  type AnyRouter,
} from '@trpc/server';

const IPC_CHANNEL = 'trpc';

/** Context passed to every tRPC procedure via the IPC bridge. */
export type TRPCContext = {
  /** The IPC sender — available for streaming chunks back to the renderer. */
  sender?: Electron.WebContents;
};

interface IPCRequest {
  method: 'request';
  operation: {
    id: number;
    type: 'query' | 'mutation' | 'subscription';
    path: string;
    input?: unknown;
  };
}

export function createIPCHandler<TRouter extends AnyRouter>({
  router,
  windows = [],
}: {
  router: TRouter;
  windows?: BrowserWindow[];
}) {
  const config = router._def._config;

  ipcMain.on(IPC_CHANNEL, async (event, message: IPCRequest) => {
    if (message.method !== 'request') return;

    const { id, type, path, input } = message.operation;

    // Deserialize input through transformer (identity by default)
    const rawInput = input !== undefined
      ? config.transformer.input.deserialize(input)
      : undefined;

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const respond = (response: any) => {
      if (event.sender.isDestroyed()) return;
      const serialised = transformTRPCResponse(config, response);
      event.reply(IPC_CHANNEL, serialised);
    };

    try {
      const result = await callTRPCProcedure({
        router,
        path,
        getRawInput: async () => rawInput,
        ctx: { sender: event.sender } satisfies TRPCContext,
        type,
        signal: undefined as unknown as AbortSignal,
        batchIndex: 0,
      });

      respond({ id, result: { type: 'data', data: result } });
    } catch (cause) {
      const error = getTRPCErrorFromUnknown(cause);
      respond({
        id,
        error: getErrorShape({
          config,
          error,
          type,
          path,
          input: rawInput,
          ctx: {},
        }),
      });
    }
  });

  // Attach additional windows later if needed
  return {
    attachWindow(_win: BrowserWindow) {
      // No per-window setup needed — ipcMain.on is global
    },
  };
}
