// Bridge to the native PollingPlugin (see
// android/app/src/main/java/chat/vojo/app/PollingPlugin.java).
//
// Drives the WorkManager-based /notifications polling fallback used on
// networks where FCM (mtalk.google.com:5228) is blocked. JS owns the
// credential + room-name cache lifecycle; native owns the periodic fetch
// and notification rendering.
//
// Web has no analogue: the Service Worker already wakes for push without
// needing a periodic poll, and browsers don't expose a 15-minute periodic
// background API anyway. The web fallback is a no-op.

import { registerPlugin } from '@capacitor/core';
import { isAndroidPlatform } from '../utils/capacitor';

export type RoomMetadataMap = Record<
  string,
  | string
  | {
      name: string;
      isDirect: boolean;
      isEncrypted: boolean;
      // MXC URL for the room's avatar (or, for a DM with no explicit room
      // avatar, the other member's avatar). Optional — empty for rooms
      // without an avatar configured. Java side resolves via auth-media
      // v1.11 + LRU bitmap cache.
      avatarMxc?: string;
    }
>;

/**
 * user_id → MXC avatar URL. Bridged to the Java side so the FCM /
 * WorkManager renderers can attach IconCompat icons to per-sender Person
 * objects in MessagingStyle (and to the self-Person anchor). Senders not
 * in this map fall through to Android's default initials/blank circle.
 */
export type UserAvatarsMap = Record<string, string>;

interface PollingPluginIface {
  saveSession(opts: { accessToken: string; homeserverUrl: string; userId?: string }): Promise<void>;
  clearSession(): Promise<void>;
  // Tolerant of both legacy (`roomId: "Display"`) and new structured shape
  // (`roomId: { name, isDirect, isEncrypted, avatarMxc }`) — the Java side
  // decides the message channel (DM vs group) and resolves the room large
  // icon based on the structured value when present.
  saveRoomNames(opts: { names: RoomMetadataMap }): Promise<void>;
  saveUserAvatars(opts: { avatars: UserAvatarsMap }): Promise<void>;
  schedule(opts: { intervalMinutes: number }): Promise<void>;
  cancel(): Promise<void>;
  dismissRoom(opts: { roomId: string }): Promise<void>;
}

const noopPlugin: PollingPluginIface = {
  saveSession: async () => undefined,
  clearSession: async () => undefined,
  saveRoomNames: async () => undefined,
  saveUserAvatars: async () => undefined,
  schedule: async () => undefined,
  cancel: async () => undefined,
  dismissRoom: async () => undefined,
};

const plugin = registerPlugin<PollingPluginIface>('Polling', {
  web: noopPlugin,
});

const guard = async <T>(fn: () => Promise<T>, fallback: T): Promise<T> => {
  if (!isAndroidPlatform()) return fallback;
  try {
    return await fn();
  } catch (err) {
    // Old APK installed before the plugin shipped, or transient bridge
    // error. Swallow — polling is a best-effort backup channel, not a
    // hard dependency on the foreground push lifecycle.
    // eslint-disable-next-line no-console
    console.warn('[polling] native call failed:', err);
    return fallback;
  }
};

export const polling = {
  saveSession: (opts: { accessToken: string; homeserverUrl: string; userId?: string }) =>
    guard(() => plugin.saveSession(opts), undefined),
  clearSession: () => guard(() => plugin.clearSession(), undefined),
  saveRoomNames: (names: RoomMetadataMap) =>
    guard(() => plugin.saveRoomNames({ names }), undefined),
  saveUserAvatars: (avatars: UserAvatarsMap) =>
    guard(() => plugin.saveUserAvatars({ avatars }), undefined),
  schedule: (intervalMinutes = 15) => guard(() => plugin.schedule({ intervalMinutes }), undefined),
  cancel: () => guard(() => plugin.cancel(), undefined),
  dismissRoom: (roomId: string) => guard(() => plugin.dismissRoom({ roomId }), undefined),
};
