/* eslint-disable @typescript-eslint/naming-convention */
import { toBase64, toHex } from "@cosmjs/encoding";
import { JsonRpcRequest } from "@cosmjs/json-rpc";

import { createJsonRpcRequest } from "../../../jsonrpc";
import { assertNotEmpty, Integer, may } from "../../encodings";
import * as requests from "../../requests";

interface HeightParam {
  readonly height?: number;
}
interface RpcHeightParam {
  readonly height?: string;
}
function encodeHeightParam(param: HeightParam): RpcHeightParam {
  return {
    height: may(Integer.encode, param.height),
  };
}

interface RpcBlockchainRequestParams {
  readonly minHeight?: string;
  readonly maxHeight?: string;
}

function encodeBlockchainRequestParams(param: requests.BlockchainRequestParams): RpcBlockchainRequestParams {
  return {
    minHeight: may(Integer.encode, param.minHeight),
    maxHeight: may(Integer.encode, param.maxHeight),
  };
}

interface RpcAbciQueryParams {
  readonly path: string;
  /** hex encoded */
  readonly data: string;
  readonly height?: string;
  readonly prove?: boolean;
}

function encodeAbciQueryParams(params: requests.AbciQueryParams): RpcAbciQueryParams {
  return {
    path: assertNotEmpty(params.path),
    data: toHex(params.data),
    height: may(Integer.encode, params.height),
    prove: params.prove,
  };
}

interface RpcBroadcastTxParams {
  /** base64 encoded */
  readonly tx: string;
}
function encodeBroadcastTxParams(params: requests.BroadcastTxParams): RpcBroadcastTxParams {
  return {
    tx: toBase64(assertNotEmpty(params.tx)),
  };
}

interface RpcTxParams {
  /** base64 encoded */
  readonly hash: string;
  readonly prove?: boolean;
}
function encodeTxParams(params: requests.TxParams): RpcTxParams {
  return {
    hash: toBase64(assertNotEmpty(params.hash)),
    prove: params.prove,
  };
}

interface RpcTxSearchParams {
  readonly query: string;
  readonly prove?: boolean;
  readonly page?: string;
  readonly per_page?: string;
}
function encodeTxSearchParams(params: requests.TxSearchParams): RpcTxSearchParams {
  return {
    query: params.query,
    prove: params.prove,
    page: may(Integer.encode, params.page),
    per_page: may(Integer.encode, params.per_page),
  };
}

interface RpcValidatorsParams {
  readonly height?: string;
  readonly page?: string;
  readonly per_page?: string;
}
function encodeValidatorsParams(params: requests.ValidatorsParams): RpcValidatorsParams {
  return {
    height: may(Integer.encode, params.height),
    page: may(Integer.encode, params.page),
    per_page: may(Integer.encode, params.per_page),
  };
}

export class Params {
  public static encodeAbciInfo(req: requests.AbciInfoRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method);
  }

  public static encodeAbciQuery(req: requests.AbciQueryRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeAbciQueryParams(req.params));
  }

  public static encodeBlock(req: requests.BlockRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeHeightParam(req.params));
  }

  public static encodeBlockchain(req: requests.BlockchainRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeBlockchainRequestParams(req.params));
  }

  public static encodeBlockResults(req: requests.BlockResultsRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeHeightParam(req.params));
  }

  public static encodeBroadcastTx(req: requests.BroadcastTxRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeBroadcastTxParams(req.params));
  }

  public static encodeCommit(req: requests.CommitRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeHeightParam(req.params));
  }

  public static encodeGenesis(req: requests.GenesisRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method);
  }

  public static encodeHealth(req: requests.HealthRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method);
  }

  public static encodeStatus(req: requests.StatusRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method);
  }

  public static encodeSubscribe(req: requests.SubscribeRequest): JsonRpcRequest {
    const eventTag = { key: "tm.event", value: req.query.type };
    const query = requests.buildQuery({ tags: [eventTag], raw: req.query.raw });
    return createJsonRpcRequest("subscribe", { query: query });
  }

  public static encodeTx(req: requests.TxRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeTxParams(req.params));
  }

  // TODO: encode params for query string???
  public static encodeTxSearch(req: requests.TxSearchRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeTxSearchParams(req.params));
  }

  public static encodeValidators(req: requests.ValidatorsRequest): JsonRpcRequest {
    return createJsonRpcRequest(req.method, encodeValidatorsParams(req.params));
  }
}
