/*
 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 
 */
/**
 * Interface to the wallet through WebExtension messaging.
 */
/**
 * Imports.
 */
import {
  AbsoluteTime,
  CoreApiResponse,
  Logger,
  LogLevel,
  NotificationType,
  TalerErrorCode,
  TalerErrorDetail,
  WalletDiagnostics,
} from "@gnu-taler/taler-util";
import {
  WalletCoreApiClient,
  WalletCoreOpKeys,
  WalletCoreRequestType,
  WalletCoreResponseType,
} from "@gnu-taler/taler-wallet-core";
import {
  MessageFromBackend,
  MessageFromFrontendBackground,
  MessageFromFrontendWallet,
} from "./platform/api.js";
import { platform } from "./platform/foreground.js";
/**
 *
 * @author sebasjm
 */
const logger = new Logger("wxApi");
export interface ExtendedPermissionsResponse {
  newValue: boolean;
}
export interface BackgroundOperations {
  freeze: {
    request: number;
    response: void;
  };
  sum: {
    request: number[];
    response: number;
  };
  resetDb: {
    request: void;
    response: void;
  };
  containsHeaderListener: {
    request: void;
    response: ExtendedPermissionsResponse;
  };
  getDiagnostics: {
    request: void;
    response: WalletDiagnostics;
  };
  toggleHeaderListener: {
    request: boolean;
    response: ExtendedPermissionsResponse;
  };
  runGarbageCollector: {
    request: void;
    response: void;
  };
  setLoggingLevel: {
    request: {
      tag?: string;
      level: LogLevel;
    };
    response: void;
  };
}
export interface BackgroundApiClient {
  call(
    operation: Op,
    payload: BackgroundOperations[Op]["request"],
  ): Promise;
}
export class BackgroundError extends Error {
  public errorDetail: TalerErrorDetail;
  constructor(title: string, e: TalerErrorDetail) {
    super(title);
    this.errorDetail = e;
  }
}
/**
 * BackgroundApiClient integration with browser platform
 */
class BackgroundApiClientImpl implements BackgroundApiClient {
  async call(
    operation: Op,
    payload: BackgroundOperations[Op]["request"],
  ): Promise {
    let response: CoreApiResponse;
    const message: MessageFromFrontendBackground = {
      channel: "background",
      operation,
      payload,
    };
    try {
      response = await platform.sendMessageToBackground(message);
    } catch (error) {
      if (error instanceof Error) {
        throw new BackgroundError(operation, {
          code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
          when: AbsoluteTime.now(),
        });
      }
      throw error;
    }
    if (response.type === "error") {
      throw new BackgroundError(
        `Background operation "${operation}" failed`,
        response.error,
      );
    }
    logger.trace("response", response);
    return response.result as any;
  }
}
/**
 * WalletCoreApiClient integration with browser platform
 */
class WalletApiClientImpl implements WalletCoreApiClient {
  async call(
    operation: Op,
    payload: WalletCoreRequestType,
  ): Promise> {
    let response: CoreApiResponse;
    try {
      const message: MessageFromFrontendWallet = {
        channel: "wallet",
        operation,
        payload,
      };
      response = await platform.sendMessageToBackground(message);
    } catch (e) {
      console.log("Error calling backend");
      throw new Error(`Error contacting backend: ${e}`);
    }
    if (response.type === "error") {
      throw new BackgroundError(
        `Wallet operation "${operation}" failed`,
        response.error,
      );
    }
    logger.trace("got response", response);
    return response.result as any;
  }
}
function onUpdateNotification(
  messageTypes: Array,
  doCallback: undefined | (() => void),
): () => void {
  //if no callback, then ignore
  if (!doCallback)
    return () => {
      return;
    };
  const onNewMessage = (message: MessageFromBackend): void => {
    const shouldNotify = messageTypes.includes(message.type);
    if (shouldNotify) {
      doCallback();
    }
  };
  return platform.listenToWalletBackground(onNewMessage);
}
export type WxApiType = {
  wallet: WalletCoreApiClient;
  background: BackgroundApiClient;
  listener: {
    onUpdateNotification: typeof onUpdateNotification;
  };
};
export const wxApi = {
  wallet: new WalletApiClientImpl(),
  background: new BackgroundApiClientImpl(),
  listener: {
    onUpdateNotification,
  },
};