// Screen / window source enumeration + on-demand high-res thumbnail fetch.
// The picker in the renderer calls `SCREEN_GET_SOURCES` once to populate
// the grid (thumbnails come back inline as data URLs from desktopCapturer,
// so no second round-trip is needed for the initial paint). When the user
// hovers a tile we can optionally refresh the thumb at a higher resolution
// via `SCREEN_GET_THUMBNAIL` — same API but a single id and 640x360 size.

import { desktopCapturer, ipcMain } from 'electron';

import { CHANNELS, type ScreenSource } from '../ipc-types';

// Renderer-driven pending source: the picker modal sets this BEFORE calling
// getDisplayMedia so our display-media handler can route the chosen source
// to LiveKit. Stored module-level (single capture in flight at a time —
// the renderer enforces this since only one picker can be open). Cleared
// on consume or on explicit null-set (cancel/error path).
let pendingShareSourceId: string | null = null;

/** Read-and-clear: returns the pending id and resets it to null in one
 *  step so the main-process display-media handler can't accidentally apply
 *  the same id twice (e.g. if a stray getDisplayMedia call fires while
 *  the picker is closed). */
export function consumePendingShareSourceId(): string | null {
  const id = pendingShareSourceId;
  pendingShareSourceId = null;
  return id;
}

/** Non-destructive read for callers that just want to know if a pending
 *  selection exists. */
export function getPendingShareSourceId(): string | null {
  return pendingShareSourceId;
}

function parseDisplayId(raw: string): number | null {
  // desktopCapturer ids for screens look like "screen:<display-id>:0". We
  // index monitors 0-based in the UI so reduce the opaque id to a small
  // integer per primary-display order. For windows there is no display
  // association, return null.
  if (!raw.startsWith('screen:')) return null;
  const parts = raw.split(':');
  const n = Number(parts[1]);
  return Number.isFinite(n) ? n : null;
}

async function enumerate(thumbWidth: number, thumbHeight: number): Promise<ScreenSource[]> {
  const sources = await desktopCapturer.getSources({
    types: ['screen', 'window'],
    thumbnailSize: { width: thumbWidth, height: thumbHeight },
    fetchWindowIcons: true,
  });
  return sources.map((src): ScreenSource => {
    const kind: 'screen' | 'window' = src.id.startsWith('screen:') ? 'screen' : 'window';
    const thumbnailDataUrl =
      src.thumbnail && !src.thumbnail.isEmpty() ? src.thumbnail.toDataURL() : null;
    const iconDataUrl =
      src.appIcon && !src.appIcon.isEmpty() ? src.appIcon.toDataURL() : null;
    return {
      id: src.id,
      name: src.name,
      kind,
      displayId: kind === 'screen' ? parseDisplayId(src.id) : null,
      thumbnailDataUrl,
      iconDataUrl,
    };
  });
}

export function register(): void {
  ipcMain.handle(CHANNELS.SCREEN_GET_SOURCES, async (): Promise<ScreenSource[]> => {
    return enumerate(320, 180);
  });

  ipcMain.handle(
    CHANNELS.SCREEN_GET_THUMBNAIL,
    async (_evt, sourceId: string): Promise<string | null> => {
      // Re-enumerate — desktopCapturer has no "fetch one by id" API. Done
      // at 640x360 so the detail view looks crisp without paying the full
      // enumeration cost more than once per hover-debounce.
      const list = await enumerate(640, 360);
      const found = list.find((s) => s.id === sourceId);
      return found?.thumbnailDataUrl ?? null;
    },
  );

  ipcMain.handle(
    CHANNELS.SCREEN_SET_PENDING_SOURCE,
    (_evt, sourceId: string | null): void => {
      // Renderer signals the chosen source id (or null to clear on cancel/
      // error). Stored until the next getDisplayMedia request comes in via
      // the display-media handler, which calls consumePendingShareSourceId
      // to read-and-clear it.
      pendingShareSourceId = sourceId;
    },
  );
}
