import {
  checkScore,
  createGame,
  getCurrentRound,
  getFirstSubmitter,
  getGameConfig,
  getGameCoords,
  getGenerators,
  getLinkedGame,
  getPlayers,
  getStatus,
  joinGame,
  linkGame,
  sendGuess,
  sendTimeout,
} from "../domain/apiMethods";

global.fetch = jest.fn();

describe("apiMethods", () => {
  describe("getStatus", () => {
    it("gets status", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              status: "healthy",
              version: "test",
            }),
        })
      );
      expect(await getStatus()).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("handles server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      expect(await getStatus()).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("handles thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      expect(await getStatus()).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("checkScore", () => {
    it("checks score", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              score: 1000,
            }),
        })
      );
      expect(await checkScore("point1", "point2")).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(checkScore("point1", "point2")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(checkScore("point1", "point2")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("getGenerators", () => {
    it("gets generators", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              generators: [],
            }),
        })
      );
      expect(await getGenerators()).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(getGenerators()).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(getGenerators()).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("createGame", () => {
    it("creates game", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              gameId: "test-game-id",
            }),
        })
      );
      expect(
        await createGame(
          "timer",
          "rounds",
          "countryLock",
          "generationMethod",
          "ruleSet"
        )
      ).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(
        createGame(
          "timer",
          "rounds",
          "countryLock",
          "generationMethod",
          "ruleSet"
        )
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(
        createGame(
          "timer",
          "rounds",
          "countryLock",
          "generationMethod",
          "ruleSet"
        )
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("getGameConfig", () => {
    it("gets game config", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              config: "test-config",
            }),
        })
      );
      expect(await getGameConfig("test-game-id")).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(getGameConfig("test-game-id")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(getGameConfig("test-game-id")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("getGameCoords", () => {
    it("gets game coords", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              coords: ["test-coord"],
            }),
        })
      );
      expect(await getGameCoords("test-game-id")).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(getGameCoords("test-game-id")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(getGameCoords("test-game-id")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("getPlayers", () => {
    it("gets game players", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              players: ["test-player"],
            }),
        })
      );
      expect(await getPlayers("test-game-id")).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(getPlayers("test-game-id")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(getPlayers("test-game-id")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("getLinkedGame", () => {
    it("gets linked game", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              linkedGame: ["test-linked-game-id"],
            }),
        })
      );
      expect(await getLinkedGame("test-game-id")).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(getLinkedGame("test-game-id")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(getLinkedGame("test-game-id")).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("linkGame", () => {
    it("links game to another", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
        })
      );
      expect(
        await linkGame("test-game-id", "test-linked-game-id")
      ).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(
        linkGame("test-game-id", "test-linked-game-id")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(
        linkGame("test-game-id", "test-linked-game-id")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("getFirstSubmitter", () => {
    it("gets first submmiter for a round", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              first: "test-player",
            }),
        })
      );
      expect(
        await getFirstSubmitter("test-game-id", "round")
      ).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("returns null on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      expect(await getFirstSubmitter("test-game-id", "round")).toBe(null);
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(
        getFirstSubmitter("test-game-id", "round")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("joinGame", () => {
    it("joins game", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              playerId: "test-player-id",
            }),
        })
      );
      expect(
        await joinGame("test-game-id", "test-player-name")
      ).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(
        joinGame("test-game-id", "test-player-name")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(
        joinGame("test-game-id", "test-player-name")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("getCurrentRound", () => {
    it("gets current round", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              round: "test-round",
            }),
        })
      );
      expect(
        await getCurrentRound("test-game-id", "test-player-id")
      ).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(
        getCurrentRound("test-game-id", "test-player-id")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(
        getCurrentRound("test-game-id", "test-player-id")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("sendGuess", () => {
    it("sends a guess", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              score: 1000,
              totalScore: 5000,
            }),
        })
      );
      expect(
        await sendGuess(
          "test-game-id",
          "test-player-id",
          "test-round",
          { lat: "lat", lng: "lng" },
          "test-time"
        )
      ).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(
        sendGuess(
          "test-game-id",
          "test-player-id",
          "test-round",
          { lat: "lat", lng: "lng" },
          "test-time"
        )
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(
        sendGuess(
          "test-game-id",
          "test-player-id",
          "test-round",
          { lat: "lat", lng: "lng" },
          "test-time"
        )
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });

  describe("sendtimeout", () => {
    it("sends a timeout for the round", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: true,
          json: () =>
            Promise.resolve({
              score: 0,
              totalScore: 2000,
            }),
        })
      );
      expect(
        await sendTimeout("test-game-id", "test-player-id", "test-round")
      ).toMatchSnapshot();
      expect(global.fetch).toMatchSnapshot();
    });
    it("throws on server error", async () => {
      global.fetch.mockImplementation(() =>
        Promise.resolve({
          ok: false,
          statusText: "failed",
        })
      );
      await expect(
        sendTimeout("test-game-id", "test-player-id", "test-round")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
    it("passes thrown error", async () => {
      global.fetch.mockImplementation(() => {
        throw new Error("error");
      });
      await expect(
        sendTimeout("test-game-id", "test-player-id", "test-round")
      ).rejects.toThrow();
      expect(global.fetch).toMatchSnapshot();
    });
  });
});
