import { RpcRequest } from "@ledgerhq/wallet-api-core";
import {
  ExchangeStartParams,
  ExchangeStartSellParams,
  ExchangeStartSwapParams,
  ExchangeStartFundParams,
} from "@ledgerhq/wallet-api-exchange-module";
import { WalletContext, WalletHandlers } from "@ledgerhq/wallet-api-server";
import { genAccount } from "../../mock/account";
import { AppBranch, AppPlatform, Visibility } from "../types";
import { handlers } from "./server";

const mockTracking = {
  startExchangeRequested: jest.fn(),
  startExchangeSuccess: jest.fn(),
  startExchangeFail: jest.fn(),
  startExchangeNoParams: jest.fn(),
  completeExchangeRequested: jest.fn(),
  completeExchangeSuccess: jest.fn(),
  completeExchangeFail: jest.fn(),
  completeExchangeNoParams: jest.fn(),
  swapPayloadRequested: jest.fn(),
  swapResponseRetrieved: jest.fn(),
};
const testAppManifest = {
  id: "12",
  name: "test",
  url: "localhost",
  homepageUrl: "localhost",
  platforms: ["desktop"] as AppPlatform[],
  apiVersion: "2",
  manifestVersion: "2",
  branch: "debug" as AppBranch,
  permissions: [],
  domains: [],
  categories: [],
  currencies: ["btc"], //"*",
  visibility: "complete" as Visibility,
  content: {
    shortDescription: {
      en: "desc",
    },
    description: {
      en: "description",
    },
  },
};

const mockUiStartExchange = jest.fn();
const mockUiCompleteExchange = jest.fn();
const mockUiSwap = jest.fn();
const mockUiError = jest.fn();
const mockIsReady = jest.fn();

const mockUiHooks = {
  "custom.exchange.start": mockUiStartExchange,
  "custom.exchange.complete": mockUiCompleteExchange,
  "custom.exchange.error": mockUiError,
  "custom.isReady": mockIsReady,
  "custom.exchange.swap": mockUiSwap,
};

// Mock converter id to send back the id received in params.
jest.mock("../converters", () => {
  return {
    getAccountIdFromWalletAccountId: (val: string) => val,
  };
});

describe("handlers", () => {
  describe("custom.exchange.start", () => {
    beforeEach(() => {
      mockUiStartExchange.mockClear();
    });

    it("calls SWAP with correct infos", async () => {
      // Given
      const accounts = [genAccount("accountId1"), genAccount("accountId2")];
      const handler = handlers({
        accounts,
        tracking: mockTracking,
        manifest: testAppManifest,
        uiHooks: mockUiHooks,
      });

      const params: ExchangeStartSwapParams = {
        exchangeType: "SWAP",
        provider: "TestProvider",
        fromAccountId: accounts[0].id,
        toAccountId: accounts[1].id,
        tokenCurrency: undefined,
      };
      const { request, context, walletHandlers } = prepareSwapRequest(params);

      mockUiStartExchange.mockImplementation(params => {
        params.onSuccess("NONCE");
      });

      // When
      const result = await handler["custom.exchange.start"](request, context, walletHandlers);

      // Then
      expect(result).toEqual({ transactionId: "NONCE" });
      expect(mockUiStartExchange).toHaveBeenCalledTimes(1);
      const receivedParams = mockUiStartExchange.mock.calls[0][0].exchangeParams;
      expect(receivedParams.exchangeType).toBe("SWAP");
      expect(receivedParams.provider).toBe("TestProvider");
      expect(receivedParams.exchange.fromAccount).toBe(accounts[0]);
      expect(receivedParams.exchange.fromParentAccount).toBe(accounts[0]);
      expect(receivedParams.exchange.toAccount).toBe(accounts[1]);
      expect(receivedParams.exchange.toParentAccount).toBe(accounts[1]);
      expect(mockUiCompleteExchange).not.toHaveBeenCalled();
    });

    it("calls SELL with correct infos", async () => {
      // Given
      const accounts = [genAccount("accountId1"), genAccount("accountId2")];
      const handler = handlers({
        accounts,
        tracking: mockTracking,
        manifest: testAppManifest,
        uiHooks: mockUiHooks,
      });

      const params: ExchangeStartSellParams = {
        exchangeType: "SELL",
        provider: "TestSellProvider",
        fromAccountId: accounts[0].id,
      };
      const { request, context, walletHandlers } = prepareSellRequest(params);

      mockUiStartExchange.mockImplementation(params => {
        params.onSuccess("NONCE");
      });

      // When
      const result = await handler["custom.exchange.start"](request, context, walletHandlers);

      // Then
      expect(result).toEqual({ transactionId: "NONCE" });
      expect(mockUiStartExchange).toHaveBeenCalledTimes(1);
      const receivedParams = mockUiStartExchange.mock.calls[0][0].exchangeParams;
      expect(receivedParams.exchangeType).toBe("SELL");
      expect(receivedParams.provider).toBe("TestSellProvider");
      expect(mockUiCompleteExchange).not.toHaveBeenCalled();
    });

    it("calls FUND with correct infos", async () => {
      // Given
      const accounts = [genAccount("accountId1"), genAccount("accountId2")];
      const handler = handlers({
        accounts,
        tracking: mockTracking,
        manifest: testAppManifest,
        uiHooks: mockUiHooks,
      });

      const params: ExchangeStartFundParams = {
        exchangeType: "FUND",
        provider: "TestFundProvider",
        fromAccountId: accounts[0].id,
      };
      const { request, context, walletHandlers } = prepareSellRequest(params);

      mockUiStartExchange.mockImplementation(params => {
        params.onSuccess("NONCE");
      });

      // When
      const result = await handler["custom.exchange.start"](request, context, walletHandlers);

      // Then
      expect(result).toEqual({ transactionId: "NONCE" });
      expect(mockUiStartExchange).toHaveBeenCalledTimes(1);
      const receivedParams = mockUiStartExchange.mock.calls[0][0].exchangeParams;
      expect(receivedParams.exchangeType).toBe("FUND");
      expect(receivedParams.provider).toBe("TestFundProvider");
      expect(mockUiCompleteExchange).not.toHaveBeenCalled();
    });
  });
});

type PreparedRequest = {
  request: RpcRequest<string, ExchangeStartParams | ExchangeStartSwapParams>;
  context: WalletContext;
  walletHandlers: Partial<WalletHandlers>;
};
function prepareSwapRequest(params: ExchangeStartSwapParams): PreparedRequest {
  const request: RpcRequest<string, ExchangeStartSwapParams> = {
    jsonrpc: "2.0",
    method: "custom.exchange.start",
    params: params,
    id: "jsonRpcRequestId",
  };

  const context = {
    config: {
      userId: "userId",
      tracking: false,
      wallet: {
        name: "wallet name",
        version: "2",
      },
      appId: "appId",
    },
  };

  return {
    request,
    context,
    walletHandlers: {},
  };
}

function prepareSellRequest(params: ExchangeStartParams): PreparedRequest {
  const request: RpcRequest<string, ExchangeStartParams> = {
    jsonrpc: "2.0",
    method: "custom.exchange.start",
    params: params,
    id: "jsonRpcRequestId",
  };

  const context = {
    config: {
      userId: "userId",
      tracking: false,
      wallet: {
        name: "wallet name",
        version: "2",
      },
      appId: "appId",
    },
  };

  return {
    request,
    context,
    walletHandlers: {},
  };
}
