import * as z from "zod";

import {
  type ClipboardDecrypter,
  ClipboardDecrypterImplementation,
} from "./clipboard-decrypter.ts";
import {type NotificationData, newNotification} from "./notification/index.ts";
import {ipcRenderer} from "./typed-ipc-renderer.ts";

type ListenerType = (...arguments_: unknown[]) => void;

/* eslint-disable @typescript-eslint/naming-convention */
export type ElectronBridge = {
  send_event: (eventName: string, ...arguments_: unknown[]) => boolean;
  on_event: (eventName: string, listener: ListenerType) => void;
  new_notification: (
    title: string,
    options: NotificationOptions,
    dispatch: (type: string, eventInit: EventInit) => boolean,
  ) => NotificationData;
  get_idle_on_system: () => boolean;
  get_last_active_on_system: () => number;
  get_send_notification_reply_message_supported: () => boolean;
  set_send_notification_reply_message_supported: (value: boolean) => void;
  decrypt_clipboard: (version: number) => ClipboardDecrypter;
};
/* eslint-enable @typescript-eslint/naming-convention */

let notificationReplySupported = false;
// Indicates if the user is idle or not
let idle = false;
// Indicates the time at which user was last active
let lastActive = Date.now();

export const bridgeEvents = new EventTarget();

export class BridgeEvent extends Event {
  constructor(
    type: string,
    public readonly arguments_: unknown[] = [],
  ) {
    super(type);
  }
}

/* eslint-disable @typescript-eslint/naming-convention */
const electron_bridge: ElectronBridge = {
  send_event: (eventName: string, ...arguments_: unknown[]): boolean =>
    bridgeEvents.dispatchEvent(new BridgeEvent(eventName, arguments_)),

  on_event(eventName: string, listener: ListenerType): void {
    bridgeEvents.addEventListener(eventName, (event) => {
      listener(...z.instanceof(BridgeEvent).parse(event).arguments_);
    });
  },

  new_notification: (
    title: string,
    options: NotificationOptions,
    dispatch: (type: string, eventInit: EventInit) => boolean,
  ): NotificationData => newNotification(title, options, dispatch),

  get_idle_on_system: (): boolean => idle,

  get_last_active_on_system: (): number => lastActive,

  get_send_notification_reply_message_supported: (): boolean =>
    notificationReplySupported,

  set_send_notification_reply_message_supported(value: boolean): void {
    notificationReplySupported = value;
  },

  decrypt_clipboard: (version: number): ClipboardDecrypter =>
    new ClipboardDecrypterImplementation(version),
};
/* eslint-enable @typescript-eslint/naming-convention */

bridgeEvents.addEventListener("total_unread_count", (event) => {
  const [unreadCount] = z
    .tuple([z.number()])
    .parse(z.instanceof(BridgeEvent).parse(event).arguments_);
  ipcRenderer.send("unread-count", unreadCount);
});

bridgeEvents.addEventListener("realm_name", (event) => {
  const [realmName] = z
    .tuple([z.string()])
    .parse(z.instanceof(BridgeEvent).parse(event).arguments_);
  const serverUrl = location.origin;
  ipcRenderer.send("realm-name-changed", serverUrl, realmName);
});

bridgeEvents.addEventListener("realm_icon_url", (event) => {
  const [iconUrl] = z
    .tuple([z.string()])
    .parse(z.instanceof(BridgeEvent).parse(event).arguments_);
  const serverUrl = location.origin;
  ipcRenderer.send(
    "realm-icon-changed",
    serverUrl,
    iconUrl.includes("http") ? iconUrl : `${serverUrl}${iconUrl}`,
  );
});

// Set user as active and update the time of last activity
ipcRenderer.on("set-active", () => {
  idle = false;
  lastActive = Date.now();
});

// Set user as idle and time of last activity is left unchanged
ipcRenderer.on("set-idle", () => {
  idle = true;
});

// This follows node's idiomatic implementation of event
// emitters to make event handling more simpler instead of using
// functions zulip side will emit event using ElectronBridge.send_event
// which is alias of .emit and on this side we can handle the data by adding
// a listener for the event.
export default electron_bridge;
