/*
 This file is part of GNU Taler
 (C) 2022 Taler Systems S.A.

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */

import { Logger, TalerUri } from "@gnu-taler/taler-util";
import { WalletOperations } from "@gnu-taler/taler-wallet-core";
import { BackgroundOperations } from "../wxApi.js";
import {
  BackgroundPlatformAPI,
  ForegroundPlatformAPI,
  MessageFromBackend,
  MessageFromFrontend,
  MessageResponse,
  defaultSettings,
} from "./api.js";

const logger = new Logger("dev.ts");

const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
  runningOnPrivateMode: () => false,
  isFirefox: () => false,
  getSettingsFromStorage: () => Promise.resolve(defaultSettings),
  keepAlive: (cb: VoidFunction) => cb(),
  findTalerUriInActiveTab: async () => undefined,
  findTalerUriInClipboard: async () => undefined,
  listenNetworkConnectionState: () => () => undefined,
  openNewURLFromPopup: () => undefined,
  triggerWalletEvent: () => undefined,
  setAlertedIcon: () => undefined,
  setNormalIcon: () => undefined,
  getPermissionsApi: () => ({
    containsClipboardPermissions: async () => true,
    removeClipboardPermissions: async () => false,
    requestClipboardPermissions: async () => false,
  }),

  getWalletWebExVersion: () => ({
    version: "none",
  }),
  notifyWhenAppIsReady: () => {
    const knownFrames = ["popup", "wallet"];
    let total = knownFrames.length;
    return new Promise((fn) => {
      function waitAndNotify(): void {
        total--;
        logger.trace(`waitAndNotify  ${total}`);
        if (total < 1) {
          fn();
        }
      }
      knownFrames.forEach((f) => {
        const theFrame = window.frames[f as any];
        if (theFrame.location.href === "about:blank") {
          waitAndNotify();
        } else {
          theFrame.addEventListener("load", waitAndNotify);
        }
      });
    });
  },

  openWalletPage: (page: string) => {
    // @ts-ignore
    window.parent.redirectWallet(`wallet.html#${page}`);
  },
  openWalletPageFromPopup: (page: string) => {
    // @ts-ignore
    window.parent.redirectWallet(`wallet.html#${page}`);
    // close the popup
    // @ts-ignore
    window.parent.closePopup();
  },
  openWalletURIFromPopup: (page: TalerUri) => {
    alert("openWalletURIFromPopup not implemented yet");
  },
  redirectTabToWalletPage: (tabId: number, page: string) => {
    alert("redirectTabToWalletPage not implemented yet");
  },
  registerAllIncomingConnections: () => undefined,
  registerOnInstalled: () => Promise.resolve(),
  registerReloadOnNewVersion: () => undefined,

  useServiceWorkerAsBackgroundProcess: () => false,

  listenToAllChannels: (
    notifyNewMessage: (message: any, from: string) => Promise<MessageResponse>,
  ) => {
    window.addEventListener(
      "message",
      (event: MessageEvent<IframeMessageType>) => {
        if (event.data.type !== "command") return;
        const sender = event.data.header.replyMe;

        notifyNewMessage(event.data.body as any, sender).then((resp) => {
          logger.trace(`listenToAllChannels: from ${sender}`, event);
          if (event.source) {
            const msg: IframeMessageResponse = {
              type: "response",
              header: { responseId: sender },
              body: resp,
            };
            window.parent.postMessage(msg);
          }
        });
      },
    );
  },
  sendMessageToAllChannels: (message: MessageFromBackend) => {
    Array.from(window.frames).forEach((w) => {
      try {
        w.postMessage({
          header: {},
          body: message,
        });
      } catch (e) {
        console.error(e);
      }
    });
  },
  listenToWalletBackground: (onNewMessage: (m: MessageFromBackend) => void) => {
    function listener(event: MessageEvent<IframeMessageType>): void {
      logger.trace(`listenToWalletBackground: `, event);
      if (event.data.type !== "notification") return;
      onNewMessage(event.data.body);
    }
    window.parent.addEventListener("message", listener);
    return () => {
      window.parent.removeEventListener("message", listener);
    };
  },

  sendMessageToBackground: async <
    Op extends WalletOperations | BackgroundOperations,
  >(
    payload: MessageFromFrontend<Op>,
  ): Promise<MessageResponse> => {
    const replyMe = `reply-${Math.floor(Math.random() * 100000)}`;
    const message: IframeMessageCommand = {
      type: "command",
      header: { replyMe },
      body: payload,
    };

    logger.trace(`sendMessageToBackground: `, message);

    return new Promise((res, rej) => {
      function listener(event: MessageEvent<IframeMessageType>): void {
        if (
          event.data.type !== "response" ||
          event.data.header.responseId !== replyMe
        ) {
          return;
        }
        res(event.data.body);
        window.parent.removeEventListener("message", listener);
      }
      window.parent.addEventListener("message", listener, {});
      window.parent.postMessage(message);
    });
  },
};

type IframeMessageType =
  | IframeMessageNotification
  | IframeMessageResponse
  | IframeMessageCommand;

interface IframeMessageNotification {
  type: "notification";
  header: Record<string, never>;
  body: MessageFromBackend;
}
interface IframeMessageResponse {
  type: "response";
  header: {
    responseId: string;
  };
  body: MessageResponse;
}

interface IframeMessageCommand {
  type: "command";
  header: {
    replyMe: string;
  };
  body: MessageFromFrontend<any>;
}

export default api;
