jest.mock("../../services/updates");

import {
  render,
  cleanup,
  screen,
  fireEvent,
  waitFor,
} from "@testing-library/react";
import { UpdatesProvider, useUpdatesContext } from "./UpdatesContext";
import { StatusProvider, useStatusContext } from "./StatusContext";
import { TEST_AUTH_OBJECT } from "../../utils/testing";

describe("UpdatesContext", () => {
  describe("getPackages", () => {
    const expectedData = `[{"id":1,"package_name":"Test","version":"1.0","link":"http://cloudfront.com/download"},{"id":2,"package_name":"Test","version":"1.1","link":"http://cloudfront.com/download"},{"id":3,"package_name":"Test","version":"1.2","link":"http://cloudfront.com/download"}]`;
    const checkState = (busy, packages, message) => {
      expect(screen.getByTestId("busy").innerHTML).toEqual(busy);
      expect(screen.getByTestId("packages").innerHTML).toEqual(packages);
      expect(screen.getByTestId("message").innerHTML).toEqual(message);
    };

    beforeEach(() => {
      const TestComp = () => {
        const { busy, packages, getPackages } = useUpdatesContext();
        const { message, setMessage } = useStatusContext();
        const exec = async (data, token) => {
          try {
            await getPackages(data, token);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="busy">{busy.toString()}</div>
            <div data-testid="packages">{JSON.stringify(packages)}</div>
            <div data-testid="message">{message}</div>
            <button
              data-testid="no-filter"
              onClick={() => {
                exec(null, TEST_AUTH_OBJECT);
              }}
            />
            <button
              data-testid="with-filter"
              onClick={() => {
                exec({ package_name: "Test" }, TEST_AUTH_OBJECT);
              }}
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <UpdatesProvider>
            <TestComp />
          </UpdatesProvider>
        </StatusProvider>
      );
    });

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

    it("Initial state", async () => {
      checkState("false", "[]", "");
    });

    it("getPackages no filter", async () => {
      fireEvent.click(screen.getByTestId("no-filter"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toBe("false")
      );
      checkState("false", expectedData, "");
    });

    it("getPackages with filter", async () => {
      fireEvent.click(screen.getByTestId("with-filter"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toBe("false")
      );
      checkState("false", expectedData, "");
    });
  });

  describe("updatePackage", () => {
    let result = null;
    const checkState = (busy, message, data) => {
      expect(screen.getByTestId("busy").innerHTML).toEqual(busy);
      expect(screen.getByTestId("message").innerHTML).toEqual(message);
      expect(result).toEqual(data);
    };

    beforeEach(() => {
      const TestComp = () => {
        const { busy, updatePackage } = useUpdatesContext();
        const { message, setMessage } = useStatusContext();
        const exec = async (data, token) => {
          var r = null;

          try {
            r = await updatePackage(data, token);
          } catch (e) {
            setMessage(e.message);
          }

          return r;
        };

        return (
          <>
            <div data-testid="busy">{busy.toString()}</div>
            <div data-testid="message">{message}</div>
            <button
              data-testid="no-data"
              onClick={async () => {
                result = await exec(null, TEST_AUTH_OBJECT);
              }}
            />
            <button
              data-testid="with-bad-data"
              onClick={async () => {
                result = await exec({ package_name: "Test" }, TEST_AUTH_OBJECT);
              }}
            />
            <button
              data-testid="with-good-data"
              onClick={async () => {
                result = await exec(
                  {
                    package_name: "Test",
                    version: "1.0",
                  },
                  TEST_AUTH_OBJECT
                );
              }}
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <UpdatesProvider>
            <TestComp />
          </UpdatesProvider>
        </StatusProvider>
      );
    });

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

    it("Initial state", () => {
      checkState("false", "", null);
    });

    it("no-data", async () => {
      fireEvent.click(screen.getByTestId("no-data"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toBe("false")
      );
      checkState("false", "No update data", null);
    });

    it("with-bad-data", async () => {
      fireEvent.click(screen.getByTestId("with-bad-data"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toBe("false")
      );
      checkState("false", "Version required", null);
    });

    it("with-good-data", async () => {
      fireEvent.click(screen.getByTestId("with-good-data"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toBe("false")
      );
      checkState("false", "", { package_name: "Test", version: "1.0" });
    });
  });

  describe("createCarUpdates", () => {
    let result = null;
    const checkState = (busy, message, data) => {
      expect(screen.getByTestId("busy").innerHTML).toEqual(busy);
      expect(screen.getByTestId("message").innerHTML).toEqual(message);
      expect(result).toEqual(data);
    };

    beforeEach(() => {
      const TestComp = () => {
        const { busy, createCarUpdates } = useUpdatesContext();
        const { message, setMessage } = useStatusContext();
        const exec = async (data, token) => {
          var r = null;

          try {
            r = await createCarUpdates(data, token);
          } catch (e) {
            setMessage(e.message);
          }

          return r;
        };

        return (
          <>
            <div data-testid="busy">{busy.toString()}</div>
            <div data-testid="message">{message}</div>
            <button
              data-testid="no-data"
              onClick={async () => {
                result = await exec(null, TEST_AUTH_OBJECT);
              }}
            />
            <button
              data-testid="with-bad-data"
              onClick={async () => {
                result = await exec(
                  { package_id: 1, vins: [] },
                  TEST_AUTH_OBJECT
                );
              }}
            />
            <button
              data-testid="with-good-data"
              onClick={async () => {
                result = await exec(
                  {
                    package_id: 1,
                    vins: ["FISKER123", "FISKER124", "FISKER125"],
                  },
                  TEST_AUTH_OBJECT
                );
              }}
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <UpdatesProvider>
            <TestComp />
          </UpdatesProvider>
        </StatusProvider>
      );
    });

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

    it("Initial state", () => {
      checkState("false", "", null);
    });

    it("no-data", async () => {
      fireEvent.click(screen.getByTestId("no-data"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toBe("false")
      );
      checkState("false", "No car update data", null);
    });

    it("with-bad-data", async () => {
      fireEvent.click(screen.getByTestId("with-bad-data"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toBe("false")
      );
      checkState("false", "Cars are required", null);
    });

    it("with-good-data", async () => {
      fireEvent.click(screen.getByTestId("with-good-data"));
      await waitFor(() =>
        expect(screen.getByTestId("busy").innerHTML).toBe("false")
      );
      checkState("false", "", {
        id: 1,
        package_id: 1,
        vins: ["FISKER123", "FISKER124", "FISKER125"],
      });
    });
  });
});
