/**
 * Renderer-side tRPC IPC link for Electron.
 *
 * Replaces electron-trpc's ipcLink with a custom implementation that
 * works with our custom IPC handler + tRPC v11.
 */
import { observable } from '@trpc/server/observable';
import type { TRPCLink } from '@trpc/client';
import type { AnyRouter } from '@trpc/server';

interface ElectronTRPC {
  sendMessage: (msg: unknown) => void;
  onMessage: (cb: (data: unknown) => void) => (() => void) | void;
}

type V3StreamEvent =
  | { type: 'stream_start'; requestId: string }
  | { type: 'stream_text'; requestId: string; chunk: string }
  | { type: 'stream_end'; requestId: string; mutations?: unknown[] }
  | {
      type: 'floating_domain';
      requestId: string;
      domain:
        | 'tasks'
        | 'notes'
        | 'timelines'
        | 'projects'
        | {
            type: 'task' | 'timeline' | 'project' | 'note' | 'node';
            id?: string | null;
            section?: 'task' | 'timeline' | 'note' | null;
          };
    };

interface ElectronAI {
  onStreamEvent: (cb: (data: V3StreamEvent) => void) => () => void;
  onBriefUpdated: (cb: (content: string) => void) => () => void;
}

interface ElectronDialog {
  showOpenDialog: (options: {
    properties?: string[];
    title?: string;
    defaultPath?: string;
    filters?: { name: string; extensions: string[] }[];
    multiSelections?: boolean;
  }) => Promise<{ canceled: boolean; filePaths: string[] }>;
}

declare global {
  interface Window {
    electronTRPC: ElectronTRPC;
    electronAI: ElectronAI;
    electronDialog: ElectronDialog;
  }
}

type TRPCResponse = {
  id: number | null;
  result?: { type: string; data?: unknown };
  error?: unknown;
};

let nextId = 0;

export function ipcLink<TRouter extends AnyRouter>(): TRPCLink<TRouter> {
  return () =>
    ({ op }) =>
      observable((observer) => {
        const id = ++nextId;
        const { electronTRPC } = window;

        if (!electronTRPC) {
          observer.error(
            new Error(
              'Could not find `electronTRPC` global. ' +
                'Check that the preload script has been loaded.',
            ) as any, // eslint-disable-line @typescript-eslint/no-explicit-any
          );
          return;
        }

        const unsubscribe = electronTRPC.onMessage((response: unknown) => {
          const msg = response as TRPCResponse;
          if (msg.id !== id) return;

          if ('error' in msg) {
            observer.error(msg.error as any); // eslint-disable-line @typescript-eslint/no-explicit-any
            return;
          }

          observer.next({
            result: msg.result as { type: 'data'; data: unknown },
          });
          observer.complete();
        });

        electronTRPC.sendMessage({
          method: 'request',
          operation: {
            id,
            type: op.type,
            path: op.path,
            input: op.input,
          },
        });

        return () => {
          if (typeof unsubscribe === 'function') unsubscribe();
        };
      });
}
