process.env.REACT_APP_MONITOR_STORE = "true";

jest.mock("../store");
jest.mock("../domain/apiMethods");
jest.mock("../domain/localStorageMethods");

import {
  getCurrentRound,
  joinGame,
  sendGuess,
  sendTimeout,
} from "../domain/apiMethods";
import { IN_ROUND, POST_GAME, PRE_ROUND } from "../domain/gameStates";
import {
  dispatch,
  useGameId,
  usePlayerName,
  useGameState,
  useCurrentRound,
  useTargetPoint,
  usePanoStartPosition,
  usePanoStartPov,
  useRoundSeconds,
  useLastRound,
} from "../domain/gameStore";
import {
  clearGameInfoFromLocalStorage,
  clearRoundInfoFromLocalStorage,
  getInfoFromLocalStorage,
  saveGameInfoToLocalStorage,
  saveTimerToLocalStorage,
} from "../domain/localStorageMethods";

describe("gameStore", () => {
  beforeEach(() => {
    dispatch.RESET_STORE();
  });
  describe("dispatch", () => {
    describe("setPlayerName", () => {
      it("sets player name", () => {
        dispatch.setPlayerName("test-player-name");
        expect(usePlayerName()).toBe("test-player-name");
      });
    });
    describe("goToLobby", () => {
      it("goes to lobby", () => {
        dispatch.goToLobby("test-game-id");
        expect(useGameId()).toBe("test-game-id");
        expect(dispatch.GET_STORED_VALUE("playerId")).toBe(null);
        expect(useGameState()).toBe(PRE_ROUND);
      });
    });
    describe("startRound", () => {
      it("starts round", () => {
        dispatch.startRound();
        expect(useGameState()).toBe(IN_ROUND);
      });
    });
    describe("goToSummary", () => {
      it("goes to summary", () => {
        dispatch.goToSummary();
        expect(useGameId()).toBe(null);
        expect(useGameState()).toBe(POST_GAME);
        expect(clearRoundInfoFromLocalStorage).toHaveBeenCalled();
        expect(clearGameInfoFromLocalStorage).toHaveBeenCalled();
      });
      it("updates game ID if given", () => {
        dispatch.goToSummary("test-game-id");
        expect(useGameId()).toBe("test-game-id");
        expect(useGameState()).toBe(POST_GAME);
        expect(clearRoundInfoFromLocalStorage).toHaveBeenCalled();
        expect(clearGameInfoFromLocalStorage).toHaveBeenCalled();
      });
      it("can avoid clearing saved data", () => {
        dispatch.goToSummary("test-game-id", false);
        expect(useGameId()).toBe("test-game-id");
        expect(useGameState()).toBe(POST_GAME);
        expect(clearRoundInfoFromLocalStorage).not.toHaveBeenCalled();
        expect(clearGameInfoFromLocalStorage).not.toHaveBeenCalled();
      });
    });
    describe("updateCurrentRound", () => {
      it("updates current round", async () => {
        getCurrentRound.mockReturnValue({
          currentRound: "round",
          coord: "coord",
          timer: "timer",
        });
        await dispatch.updateCurrentRound();
        expect(useCurrentRound()).toBe("round");
        expect(useTargetPoint()).toBe("coord");
        expect(usePanoStartPosition()).toBe("coord");
        expect(usePanoStartPov()).toMatchObject({
          heading: 0,
          pitch: 0,
        });
        expect(useRoundSeconds()).toBe("timer");
        expect(getCurrentRound).toMatchSnapshot();
      });
    });
    describe("joinGame", () => {
      it("joins game then updates current round", async () => {
        dispatch.SET_STORED_VALUE("gameId", "test-game-id");
        dispatch.SET_STORED_VALUE("playerName", "test-player-name");
        joinGame.mockReturnValue({
          playerId: "test-player-id",
        });
        getCurrentRound.mockReturnValue({
          currentRound: "round",
          coord: "coord",
          timer: "timer",
        });
        await dispatch.joinGame();
        expect(joinGame).toMatchSnapshot();
        expect(saveGameInfoToLocalStorage).toMatchSnapshot();
        expect(dispatch.GET_STORED_VALUE("playerId")).toBe("test-player-id");
        expect(useCurrentRound()).toBe("round");
        expect(useTargetPoint()).toBe("coord");
        expect(usePanoStartPosition()).toBe("coord");
        expect(usePanoStartPov()).toMatchObject({
          heading: 0,
          pitch: 0,
        });
        expect(useRoundSeconds()).toBe("timer");
        expect(getCurrentRound).toMatchSnapshot();
      });
    });
    describe("rejoinGame", () => {
      it("rejoins game then updates current round", async () => {
        getInfoFromLocalStorage.mockReturnValue({
          gameId: "test-game-id",
          playerName: "test-player-name",
          playerId: "test-player-id",
          timer: "test-timer",
          position: "test-position",
          pov: "test-pov",
        });
        getCurrentRound.mockReturnValue({
          currentRound: "round",
          coord: "coord",
          timer: "timer",
        });
        await dispatch.rejoinGame();
        expect(saveGameInfoToLocalStorage).toMatchSnapshot();
        expect(useGameId()).toBe("test-game-id");
        expect(usePlayerName()).toBe("test-player-name");
        expect(dispatch.GET_STORED_VALUE("playerId")).toBe("test-player-id");
        expect(useCurrentRound()).toBe("round");
        expect(useTargetPoint()).toBe("coord");
        expect(usePanoStartPosition()).toBe("test-position");
        expect(usePanoStartPov()).toBe("test-pov");
        expect(useRoundSeconds()).toBe("test-timer");
        expect(useGameState()).toBe(IN_ROUND);
        expect(getCurrentRound).toMatchSnapshot();
      });
      it("rejoins game then updates current round even with missing information", async () => {
        getInfoFromLocalStorage.mockReturnValue({
          gameId: "test-game-id",
          playerName: "test-player-name",
          playerId: "test-player-id",
        });
        getCurrentRound.mockReturnValue({
          currentRound: "round",
          coord: "coord",
          timer: "timer",
        });
        await dispatch.rejoinGame();
        expect(saveGameInfoToLocalStorage).toMatchSnapshot();
        expect(useGameId()).toBe("test-game-id");
        expect(usePlayerName()).toBe("test-player-name");
        expect(dispatch.GET_STORED_VALUE("playerId")).toBe("test-player-id");
        expect(useCurrentRound()).toBe("round");
        expect(useTargetPoint()).toBe("coord");
        expect(usePanoStartPosition()).toBe("coord");
        expect(usePanoStartPov()).toMatchObject({
          heading: 0,
          pitch: 0,
        });
        expect(useRoundSeconds()).toBe("timer");
        expect(useGameState()).toBe(IN_ROUND);
        expect(getCurrentRound).toMatchSnapshot();
      });
    });
    describe("submitGuess", () => {
      it("submits guess and then updates the current round", async () => {
        dispatch.SET_STORED_VALUE("gameId", "test-game-id");
        dispatch.SET_STORED_VALUE("playerName", "test-player-name");
        dispatch.SET_STORED_VALUE("currentRound", "prev-round");
        dispatch.SET_STORED_VALUE("roundSeconds", 200);
        dispatch.SET_STORED_VALUE("targetPoint", "prev-target");
        sendGuess.mockReturnValue({
          score: 1000,
          totalScore: 2000,
        });
        getCurrentRound.mockReturnValue({
          currentRound: "round",
          coord: "coord",
          timer: "timer",
        });
        await dispatch.submitGuess("selected");
        expect(sendGuess).toMatchSnapshot();
        expect(clearRoundInfoFromLocalStorage).toMatchSnapshot();
        expect(useLastRound()).toMatchObject({
          roundNum: "prev-round",
          targetPoint: "prev-target",
          score: 1000,
          totalScore: 2000,
        });
        expect(useCurrentRound()).toBe("round");
        expect(useTargetPoint()).toBe("coord");
        expect(usePanoStartPosition()).toBe("coord");
        expect(usePanoStartPov()).toMatchObject({
          heading: 0,
          pitch: 0,
        });
        expect(useRoundSeconds()).toBe("timer");
        expect(getCurrentRound).toMatchSnapshot();
      });
      it("submits a timeout and then updates the current round", async () => {
        dispatch.SET_STORED_VALUE("gameId", "test-game-id");
        dispatch.SET_STORED_VALUE("playerName", "test-player-name");
        dispatch.SET_STORED_VALUE("currentRound", "prev-round");
        dispatch.SET_STORED_VALUE("targetPoint", "prev-target");
        sendTimeout.mockReturnValue({
          score: 0,
          totalScore: 2000,
        });
        getCurrentRound.mockReturnValue({
          currentRound: "round",
          coord: "coord",
          timer: "timer",
        });
        await dispatch.submitGuess(null);
        expect(sendTimeout).toMatchSnapshot();
        expect(clearRoundInfoFromLocalStorage).toMatchSnapshot();
        expect(useLastRound()).toMatchObject({
          roundNum: "prev-round",
          targetPoint: "prev-target",
          score: 0,
          totalScore: 2000,
        });
        expect(useCurrentRound()).toBe("round");
        expect(useTargetPoint()).toBe("coord");
        expect(usePanoStartPosition()).toBe("coord");
        expect(usePanoStartPov()).toMatchObject({
          heading: 0,
          pitch: 0,
        });
        expect(useRoundSeconds()).toBe("timer");
        expect(getCurrentRound).toMatchSnapshot();
      });
    });
    describe("updateRoundSeconds", () => {
      it("applies an update to round seconds", () => {
        dispatch.SET_STORED_VALUE("roundSeconds", 10);
        dispatch.updateRoundSeconds(c => c - 1);
        expect(useRoundSeconds()).toBe(9);
        expect(saveTimerToLocalStorage).toMatchSnapshot();
      });
    });
  });
});
