/*
 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 {
  CoreApiResponse,
  TalerUri,
  WalletNotification,
  WalletRunConfig,
} from "@gnu-taler/taler-util";
import { WalletOperations } from "@gnu-taler/taler-wallet-core";
import {
  ExtensionOperations,
  MessageFromExtension,
} from "../taler-wallet-interaction-loader.js";
import { BackgroundOperations } from "../wxApi.js";

export interface Permissions {
  /**
   * List of named permissions.
   */
  permissions?: string[] | undefined;
  /**
   * List of origin permissions. Anything listed here must be a subset of a
   * host that appears in the optional_permissions list in the manifest.
   *
   */
  origins?: string[] | undefined;
}

/**
 * Compatibility API that works on multiple browsers.
 */
export interface CrossBrowserPermissionsApi {
  containsClipboardPermissions(): Promise<boolean>;
  requestClipboardPermissions(): Promise<boolean>;
  removeClipboardPermissions(): Promise<boolean>;
}

export enum ExtensionNotificationType {
  SettingsChange = "settings-change",
  ClearNotifications = "clear-notifications",
}

export interface SettingsChangeNotification {
  type: ExtensionNotificationType.SettingsChange;

  currentValue: Settings;
}
export interface ClearNotificaitonNotification {
  type: ExtensionNotificationType.ClearNotifications;
}

export type ExtensionNotification =
  | SettingsChangeNotification
  | ClearNotificaitonNotification;

export type MessageFromBackend =
  | {
      type: "wallet";
      notification: WalletNotification;
    }
  | {
      type: "web-extension";
      notification: ExtensionNotification;
    };

export type MessageFromFrontend<
  Op extends BackgroundOperations | WalletOperations | ExtensionOperations,
> = Op extends BackgroundOperations
  ? MessageFromFrontendBackground<keyof BackgroundOperations>
  : Op extends ExtensionOperations
    ? MessageFromExtension<keyof ExtensionOperations>
    : Op extends WalletOperations
      ? MessageFromFrontendWallet<keyof WalletOperations>
      : never;

export type MessageFromFrontendBackground<
  Op extends keyof BackgroundOperations,
> = {
  channel: "background";
  operation: Op;
  payload: BackgroundOperations[Op]["request"];
};

export type MessageFromFrontendWallet<Op extends keyof WalletOperations> = {
  channel: "wallet";
  operation: Op;
  payload: WalletOperations[Op]["request"];
};

export type MessageResponse = CoreApiResponse;

export interface WalletWebExVersion {
  version_name?: string | undefined;
  version: string;
}

type F = WalletRunConfig["features"];
type WebexWalletConfig = {
  [P in keyof F as `wallet${Capitalize<P>}`]: F[P];
};

export interface Settings extends WebexWalletConfig {
  injectTalerSupport: boolean;
  autoOpen: boolean;
  advancedMode: boolean;
  backup: boolean;
  langSelector: boolean;
  showJsonOnError: boolean;
  extendedAccountTypes: boolean;
  showRefeshTransactions: boolean;
  suspendIndividualTransaction: boolean;
  showExchangeManagement: boolean;
  selectTosFormat: boolean;
  showWalletActivity: boolean;
}

export const defaultSettings: Settings = {
  injectTalerSupport: true,
  autoOpen: true,
  advancedMode: false,
  backup: false,
  langSelector: false,
  showRefeshTransactions: false,
  suspendIndividualTransaction: false,
  showJsonOnError: false,
  extendedAccountTypes: false,
  showExchangeManagement: false,
  walletAllowHttp: false,
  selectTosFormat: false,
  showWalletActivity: false,
  walletEnableV1Contracts: false,
};

/**
 * Compatibility helpers needed for browsers that don't implement
 * WebExtension APIs consistently.
 */
export interface BackgroundPlatformAPI {
  /**
   *
   */
  getSettingsFromStorage(): Promise<Settings>;
  /**
   * Guarantee that the service workers don't die
   */
  keepAlive(cb: VoidFunction): void;
  /**
   * FIXME: should not be needed
   *
   * check if the platform is firefox
   */
  isFirefox(): boolean;

  registerOnInstalled(callback: () => void): void;

  /**
   *
   * Check if background process run as service worker. This is used from the
   * wallet use different http api and crypto worker.
   */
  useServiceWorkerAsBackgroundProcess(): boolean;
  /**
   *
   * Open a page into the wallet UI
   * @param page
   */
  openWalletPage(page: string): void;
  /**
   *
   * Register a callback to be called when the wallet is ready to start
   * @param callback
   */
  notifyWhenAppIsReady(): Promise<void>;

  /**
   * Get the wallet version from manifest
   */
  getWalletWebExVersion(): WalletWebExVersion;
  /**
   * Backend API
   */
  registerAllIncomingConnections(): void;
  /**
   * Backend API
   */
  registerReloadOnNewVersion(): void;

  /**
   * Permission API for checking and add a listener
   */
  getPermissionsApi(): CrossBrowserPermissionsApi;
  /**
   * Used by the wallet backend to send notification about new information
   * @param message
   */
  sendMessageToAllChannels(message: MessageFromBackend): void;

  /**
   * Backend API
   *
   * When a tab has been detected to have a Taler action the background process
   * can use this function to redirect the tab to the wallet UI
   *
   * @param tabId
   * @param page
   */
  redirectTabToWalletPage(tabId: number, page: string): void;
  /**
   * Use by the wallet backend to receive operations from frontend (popup & wallet)
   * and send a response back.
   *
   * @param onNewMessage
   */
  listenToAllChannels(
    notifyNewMessage: <Op extends WalletOperations | BackgroundOperations>(
      message: MessageFromFrontend<Op> & { id: string },
      from: string,
    ) => Promise<MessageResponse>,
  ): void;

  /**
   * Change web extension Icon
   */
  setAlertedIcon(): void;
  setNormalIcon(): void;
}

export interface ForegroundPlatformAPI {
  /**
   * Check if the extension is running under
   * chrome incognito or firefox private mode.
   */
  runningOnPrivateMode(): boolean;
  /**
   * FIXME: should not be needed
   *
   * check if the platform is firefox
   */
  isFirefox(): boolean;

  /**
   * Permission API for checking and add a listener
   */
  getPermissionsApi(): CrossBrowserPermissionsApi;

  /**
   * Popup API
   *
   * Used when an TalerURI is found and open up from the popup UI.
   * Closes the popup and open the URI into the wallet UI.
   *
   * @param talerUri
   */
  openWalletURIFromPopup(talerUri: TalerUri): void;

  /**
   * Popup API
   *
   * Open a page into the wallet UI and close the popup
   * @param page
   */
  openWalletPageFromPopup(page: string): void;

  /**
   * Open a page and close the popup
   * @param url
   */
  openNewURLFromPopup(url: URL): void;
  /**
   * Get the wallet version from manifest
   */
  getWalletWebExVersion(): WalletWebExVersion;

  /**
   * Popup API
   *
   * Read the current tab html and try to find any Taler URI or QR code present.
   *
   * @return Taler URI if found
   */
  findTalerUriInActiveTab(): Promise<string | undefined>;

  /**
   * Popup API
   *
   * Read the current tab html and try to find any Taler URI or QR code present.
   *
   * @return Taler URI if found
   */
  findTalerUriInClipboard(): Promise<string | undefined>;

  /**
   * Used from the frontend to send commands to the wallet
   *
   * @param operation
   * @param payload
   *
   * @return response from the backend
   */
  sendMessageToBackground<Op extends WalletOperations | BackgroundOperations>(
    message: MessageFromFrontend<Op>,
  ): Promise<MessageResponse>;

  /**
   * Used by the wallet frontend to send notification about new information
   * @param message
   */
  triggerWalletEvent(message: MessageFromBackend): void;

  /**
   * Used from the frontend to receive notifications about new information
   * @param listener
   * @return function to unsubscribe the listener
   */
  listenToWalletBackground(
    listener: (message: MessageFromBackend) => void,
  ): () => void;

  /**
   * Notify when platform went offline
   */
  listenNetworkConnectionState(
    listener: (state: "on" | "off") => void,
  ): () => void;
}
