/*
 This file is part of GNU Taler
 (C) 2022-2024 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 {
  HttpRequestLibrary,
  makeBasicAuthHeader,
  readTalerErrorResponse,
} from "../http-common.js";
import { HttpStatusCode } from "../http-status-codes.js";
import { createPlatformHttpLib } from "../http.js";
import {
  FailCasesByMethod,
  ResultByMethod,
  opFixedSuccess,
  opKnownHttpFailure,
  opSuccessFromHttp,
  opUnknownFailure,
} from "../operation.js";
import {
  codecForAddIncomingResponse,
  codecForIncomingHistory,
  codecForOutgoingHistory,
  codecForTransferResponse,
} from "../types-taler-wire-gateway.js";
import { addLongPollingParam, addPaginationParams } from "./utils.js";

import { LongPollParams, PaginationParams } from "../types-taler-common.js";
import * as TalerWireGatewayApi from "../types-taler-wire-gateway.js";

export type TalerWireGatewayResultByMethod<
  prop extends keyof TalerWireGatewayHttpClient,
> = ResultByMethod<TalerWireGatewayHttpClient, prop>;
export type TalerWireGatewayErrorsByMethod<
  prop extends keyof TalerWireGatewayHttpClient,
> = FailCasesByMethod<TalerWireGatewayHttpClient, prop>;

/**
 * The API is used by the exchange to trigger transactions and query
 * incoming transactions, as well as by the auditor to query incoming
 * and outgoing transactions.
 *
 * https://docs.taler.net/core/api-bank-wire.html
 */
export class TalerWireGatewayHttpClient {
  httpLib: HttpRequestLibrary;

  constructor(
    readonly baseUrl: string,
    readonly username: string,
    httpClient?: HttpRequestLibrary,
  ) {
    this.httpLib = httpClient ?? createPlatformHttpLib();
  }
  // public readonly PROTOCOL_VERSION = "4:0:0";
  // isCompatible(version: string): boolean {
  //   const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version)
  //   return compare?.compatible ?? false
  // }

  // /**
  //  * https://docs.taler.net/core/api-corebank.html#config
  //  *
  //  */
  // async getConfig() {
  //   const url = new URL(`config`, this.baseUrl);
  //   const resp = await this.httpLib.fetch(url.href, {
  //     method: "GET"
  //   });
  //   switch (resp.status) {
  //     case HttpStatusCode.Ok: return opSuccess(resp, codecForCoreBankConfig())
  //     default: return opUnknownFailure(resp, await readTalerErrorResponse(resp))
  //   }
  // }

  /**
   * https://docs.taler.net/core/api-bank-wire.html#post--transfer
   *
   */
  async transfer(auth: string, body: TalerWireGatewayApi.TransferRequest) {
    const url = new URL(`transfer`, this.baseUrl);
    const resp = await this.httpLib.fetch(url.href, {
      method: "POST",
      headers: {
        Authorization: makeBasicAuthHeader(this.username, auth),
      },
      body,
    });
    switch (resp.status) {
      case HttpStatusCode.Ok:
        return opSuccessFromHttp(resp, codecForTransferResponse());
      //FIXME: show more details in docs
      case HttpStatusCode.BadRequest:
        return opKnownHttpFailure(resp.status, resp);
      case HttpStatusCode.Unauthorized:
        return opKnownHttpFailure(resp.status, resp);
      //FIXME: show more details in docs
      case HttpStatusCode.NotFound:
        return opKnownHttpFailure(resp.status, resp);
      case HttpStatusCode.Conflict:
        return opKnownHttpFailure(resp.status, resp);
      default:
        return opUnknownFailure(resp, await readTalerErrorResponse(resp));
    }
  }

  /**
   * https://docs.taler.net/core/api-bank-wire.html#get--history-incoming
   *
   */
  async getHistoryIncoming(
    auth: string,
    params?: PaginationParams & LongPollParams,
  ) {
    const url = new URL(`history/incoming`, this.baseUrl);
    addPaginationParams(url, params);
    addLongPollingParam(url, params);
    const resp = await this.httpLib.fetch(url.href, {
      method: "GET",
      headers: {
        Authorization: makeBasicAuthHeader(this.username, auth),
      },
    });
    switch (resp.status) {
      case HttpStatusCode.Ok:
        return opSuccessFromHttp(resp, codecForIncomingHistory());
      //FIXME: account should not be returned or make it optional
      case HttpStatusCode.NoContent:
        return opFixedSuccess({
          incoming_transactions: [],
          credit_account: undefined,
        });
      //FIXME: show more details in docs
      case HttpStatusCode.BadRequest:
        return opKnownHttpFailure(resp.status, resp);
      case HttpStatusCode.Unauthorized:
        return opKnownHttpFailure(resp.status, resp);
      //FIXME: show more details in docs
      case HttpStatusCode.NotFound:
        return opKnownHttpFailure(resp.status, resp);
      default:
        return opUnknownFailure(resp, await readTalerErrorResponse(resp));
    }
  }

  /**
   * https://docs.taler.net/core/api-bank-wire.html#get--history-outgoing
   *
   */
  async getHistoryOutgoing(
    auth: string,
    params?: PaginationParams & LongPollParams,
  ) {
    const url = new URL(`history/outgoing`, this.baseUrl);
    addPaginationParams(url, params);
    addLongPollingParam(url, params);
    const resp = await this.httpLib.fetch(url.href, {
      method: "GET",
      headers: {
        Authorization: makeBasicAuthHeader(this.username, auth),
      },
    });
    switch (resp.status) {
      case HttpStatusCode.Ok:
        return opSuccessFromHttp(resp, codecForOutgoingHistory());
      //FIXME: account should not be returned or make it optional
      case HttpStatusCode.NoContent:
        return opFixedSuccess({
          outgoing_transactions: [],
          debit_account: undefined,
        });
      //FIXME: show more details in docs
      case HttpStatusCode.BadRequest:
        return opKnownHttpFailure(resp.status, resp);
      case HttpStatusCode.Unauthorized:
        return opKnownHttpFailure(resp.status, resp);
      //FIXME: show more details in docs
      case HttpStatusCode.NotFound:
        return opKnownHttpFailure(resp.status, resp);
      default:
        return opUnknownFailure(resp, await readTalerErrorResponse(resp));
    }
  }

  /**
   * https://docs.taler.net/core/api-bank-wire.html#post--admin-add-incoming
   *
   */
  async addIncoming(
    auth: string,
    body: TalerWireGatewayApi.AddIncomingRequest,
  ) {
    const url = new URL(`admin/add-incoming`, this.baseUrl);
    const resp = await this.httpLib.fetch(url.href, {
      method: "POST",
      headers: {
        Authorization: makeBasicAuthHeader(this.username, auth),
      },
      body,
    });
    switch (resp.status) {
      case HttpStatusCode.Ok:
        return opSuccessFromHttp(resp, codecForAddIncomingResponse());
      //FIXME: show more details in docs
      case HttpStatusCode.BadRequest:
        return opKnownHttpFailure(resp.status, resp);
      case HttpStatusCode.Unauthorized:
        return opKnownHttpFailure(resp.status, resp);
      //FIXME: show more details in docs
      case HttpStatusCode.NotFound:
        return opKnownHttpFailure(resp.status, resp);
      case HttpStatusCode.Conflict:
        return opKnownHttpFailure(resp.status, resp);
      default:
        return opUnknownFailure(resp, await readTalerErrorResponse(resp));
    }
  }
}
