jest.mock("../../services/fleetsAPI")

import {
  render,
  cleanup,
  screen,
  fireEvent,
  waitFor,
} from "@testing-library/react";
import { FleetProvider, useFleetContext } from "./FleetContext";
import { StatusProvider, useStatusContext } from "./StatusContext";

const checkFleetResults = (error, busy, fleets) => {
  checkBaseResults(error, busy);
  expect(screen.getByTestId("fleets").innerHTML).toEqual(fleets);
};

const checkFleetVehicleResults = (error, busy, vehicles) => {
  checkBaseResults(error, busy);
  expect(screen.getByTestId("fleet-vehicles").innerHTML).toEqual(vehicles);
}

const checkBaseResults = (error, busy) => {
  expect(screen.getByTestId("error").innerHTML).toEqual(error);
  expect(screen.getByTestId("busy").innerHTML).toEqual(busy);
};

describe("FleetContext", () => {
  describe("getFleets", () => {
    beforeEach(() => {
      const TestComp = () => {
        const { busy, error, fleets, getFleets } = useFleetContext();

        return (
          <>
            <div data-testid="error">{error}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <div data-testid="fleets">{JSON.stringify(fleets)}</div>
            <button
              data-testid="getFleets"
              onClick={() => getFleets()}
            />
          </>
        );
      };
      render(
        <FleetProvider>
          <TestComp />
        </FleetProvider>
      );
    });

    afterEach(() => {
      cleanup();
    });

    it("initial state", () => {
      checkFleetResults("", "false", "[]");
    });

    it("getFleets", async () => {
      fireEvent.click(screen.getByTestId("getFleets"));
      await waitFor(() =>
        expect(screen.getByTestId("fleets").innerHTML).not.toBe("[]")
      );
      checkFleetResults("", "false", JSON.stringify(expectedFleetsData));
    });
  });

  describe("addFleet", () => {
    beforeEach(async () => {
      const TestComp = () => {
        const { busy, addFleet } = useFleetContext();
        const { message, setMessage } = useStatusContext();
        const add = async (fleet) => {
          try {
            await addFleet(fleet);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="error">{message}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <button data-testid="addFleetNull" onClick={() => add(null)} />
            <button data-testid="addFleetNoName" onClick={() => add({})} />
            <button
              data-testid="addFleet"
              onClick={() =>
                add({ name: "EU-WEST", log_level: "warn", canbus: { enabled: false } })
              }
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <FleetProvider>
            <TestComp />
          </FleetProvider>
        </StatusProvider>
      );
    });

    afterEach(() => {
      cleanup();
    });

    it("initial state", () => {
      checkBaseResults("", "false");
    });

    it("addFleetNull", async () => {
      fireEvent.click(screen.getByTestId("addFleetNull"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("No fleet data", "false");
    });

    it("addFleetNoName", async () => {
      fireEvent.click(screen.getByTestId("addFleetNoName"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("Invalid name", "false");
    });

    it("addFleet", async () => {
      fireEvent.click(screen.getByTestId("addFleet"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("", "false");
    });
  });

  describe("updateFleet", () => {
    beforeEach(async () => {
      const TestComp = () => {
        const { busy, updateFleet } = useFleetContext();
        const { message, setMessage } = useStatusContext();
        const update = async (data) => {
          try {
            await updateFleet("EU-WEST", data);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="error">{message}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <button data-testid="updateFleetNull" onClick={() => update(null)} />
            <button data-testid="updateFleetNoName" onClick={() => update({})} />
            <button
              data-testid="updateFleet"
              onClick={() =>
                update({ name: "EU-WEST", log_level: "warn", canbus: { enabled: false } })
              }
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <FleetProvider>
            <TestComp />
          </FleetProvider>
        </StatusProvider>
      );
    });

    afterEach(() => {
      cleanup();
    });

    it("initial state", () => {
      checkBaseResults("", "false");
    });

    it("updateFleetNull", async () => {
      fireEvent.click(screen.getByTestId("updateFleetNull"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("No fleet data", "false");
    });

    it("updateFleetNoName", async () => {
      fireEvent.click(screen.getByTestId("updateFleetNoName"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("Invalid name", "false");
    });

    it("updateFleet", async () => {
      fireEvent.click(screen.getByTestId("updateFleet"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("", "false");
    });
  });

  describe("deleteFleet", () => {
    beforeEach(async () => {
      const TestComp = () => {
        const { busy, deleteFleet } = useFleetContext();
        const { message, setMessage } = useStatusContext();
        const deleteF = async (name) => {
          try {
            await deleteFleet(name);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="error">{message}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <button data-testid="deleteFleetNull" onClick={() => deleteF(null)} />
            <button data-testid="deleteFleetNonexistent" onClick={() => deleteF("INVALID")} />
            <button
              data-testid="deleteFleet"
              onClick={() =>
                deleteF("US-WEST")
              }
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <FleetProvider>
            <TestComp />
          </FleetProvider>
        </StatusProvider>
      );
    });

    afterEach(() => {
      cleanup();
    });

    it("initial state", () => {
      checkBaseResults("", "false");
    });

    it("deleteFleetNull", async () => {
      fireEvent.click(screen.getByTestId("deleteFleetNull"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("Invalid name", "false");
    });

    it("deleteFleetNonexistent", async () => {
      fireEvent.click(screen.getByTestId("deleteFleetNonexistent"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("", "false");
    });

    it("deleteFleet", async () => {
      fireEvent.click(screen.getByTestId("deleteFleet"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("", "false");
    });
  });

  describe("getFleetVehicles", () => {
    beforeEach(() => {
      const TestComp = () => {
        const { busy, error, fleetVehicles, getFleetVehicles } = useFleetContext();

        return (
          <>
            <div data-testid="error">{error}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <div data-testid="fleet-vehicles">{JSON.stringify(fleetVehicles)}</div>
            <button
              data-testid="getFleetVehicles"
              onClick={() => getFleetVehicles("US-WEST")}
            />
          </>
        );
      };
      render(
        <FleetProvider>
          <TestComp />
        </FleetProvider>
      );
    });

    afterEach(() => {
      cleanup();
    });

    it("initial state", () => {
      checkFleetVehicleResults("", "false", "[]");
    });

    it("getFleetVehicles", async () => {
      fireEvent.click(screen.getByTestId("getFleetVehicles"));
      await waitFor(() =>
        expect(screen.getByTestId("fleet-vehicles").innerHTML).not.toBe("[]")
      );
      checkFleetVehicleResults("", "false", JSON.stringify(expectedFleetVehiclesData));
    });
  });

  describe("addFleetVehicle", () => {
    beforeEach(async () => {
      const TestComp = () => {
        const { busy, addFleetVehicle } = useFleetContext();
        const { message, setMessage } = useStatusContext();
        const add = async (name, vehicle) => {
          try {
            await addFleetVehicle(name, vehicle);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="error">{message}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <button data-testid="addFleetVehicleNull" onClick={() => add(null)} />
            <button data-testid="addFleetVehicleNoName" onClick={() => add({})} />
            <button
              data-testid="addFleetVehicle"
              onClick={() =>
                add("US-TEST", { vin: "TESTVIN1234567890" })
              }
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <FleetProvider>
            <TestComp />
          </FleetProvider>
        </StatusProvider>
      );
    });

    afterEach(() => {
      cleanup();
    });

    it("initial state", () => {
      checkBaseResults("", "false");
    });

    it("addFleetVehicleNull", async () => {
      fireEvent.click(screen.getByTestId("addFleetVehicleNull"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("Invalid name", "false");
    });

    it("addFleetVehicleNoName", async () => {
      fireEvent.click(screen.getByTestId("addFleetVehicleNoName"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("Invalid name", "false");
    });

    it("addFleetVehicle", async () => {
      fireEvent.click(screen.getByTestId("addFleetVehicle"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("", "false");
    });
  });

  describe("deleteFleetVehicle", () => {
    beforeEach(async () => {
      const TestComp = () => {
        const { busy, deleteFleetVehicle } = useFleetContext();
        const { message, setMessage } = useStatusContext();
        const deleteFV = async (name, vehicle) => {
          try {
            await deleteFleetVehicle(name, vehicle);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="error">{message}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <button data-testid="deleteFleetVehicleNull" onClick={() => deleteFV("US-WEST", null)} />
            <button data-testid="deleteFleetVehicleInvalid" onClick={() => deleteFV("US-WEST", "INVALID")} />
            <button
              data-testid="deleteFleetVehicle"
              onClick={() =>
                deleteFV("US-WEST", { vin: "USWESTVIN12345678" })
              }
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <FleetProvider>
            <TestComp />
          </FleetProvider>
        </StatusProvider>
      );
    });

    afterEach(() => {
      cleanup();
    });

    it("initial state", () => {
      checkBaseResults("", "false");
    });

    it("deleteFleetVehicleNull", async () => {
      fireEvent.click(screen.getByTestId("deleteFleetVehicleNull"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("Cannot read property 'vin' of null", "false");
    });

    it("deleteFleetVehicleNonexistent", async () => {
      fireEvent.click(screen.getByTestId("deleteFleetVehicleInvalid"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("Invalid VIN", "false");
    });

    it("deleteFleetVehicle", async () => {
      fireEvent.click(screen.getByTestId("deleteFleetVehicle"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toEqual("false")
      );
      checkBaseResults("", "false");
    });
  });
});

const expectedFleetsData = [
  {
    name: "US-WEST",
    log_level: "info",
    canbus: { enabled: true },
    vehicles: ["USWESTVIN12345678", "USWESTVIN12345679", "USWESTVIN12345670"]
  },
  {
    name: "US-CENTRAL",
    log_level: "warn",
    canbus: { enabled: false },
    vehicles: ["USCENTVIN12345678", "USCENTVIN12345679", "USCENTVIN12345670"]
  },
  {
    name: "US-EAST",
    log_level: "error",
    canbus: { enabled: true },
    vehicles: ["USEASTVIN12345678", "USEASTVIN12345679", "USEASTVIN12345670"]
  },
];

const expectedFleetVehiclesData = ["USWESTVIN12345678", "USWESTVIN12345679", "USWESTVIN12345670"]
