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

import {
  render,
  cleanup,
  screen,
  fireEvent,
  waitFor,
} from "@testing-library/react";
import { CANFiltersProvider, useCANFiltersContext } from "./CANFiltersContext";
import { StatusProvider, useStatusContext } from "./StatusContext";

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

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

describe("CANFiltersContext", () => {
  describe("getFilters", () => {
    beforeEach(() => {
      const TestComp = () => {
        const { busy, error, filters, getFilters } = useCANFiltersContext();

        return (
          <>
            <div data-testid="error">{error}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <div data-testid="filters">{JSON.stringify(filters)}</div>
            <button
              data-testid="getFilters"
              onClick={() => getFilters("TESTVIN1234567890")}
            />
          </>
        );
      };
      render(
        <CANFiltersProvider>
          <TestComp />
        </CANFiltersProvider>
      );
    });

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

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

    it("getFilters", async () => {
      fireEvent.click(screen.getByTestId("getFilters"));
      await waitFor(() =>
        expect(screen.getByTestId("filters").innerHTML).not.toBe("[]")
      );
      checkFiltersResults("", "false", JSON.stringify(expectedFiltersData));
    });
  });

  describe("addFilter", () => {
    beforeEach(async () => {
      const TestComp = () => {
        const { busy, addFilter } = useCANFiltersContext();
        const { message, setMessage } = useStatusContext();
        const add = async (data) => {
          try {
            await addFilter("TESTVIN1234567890", data);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="error">{message}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <button data-testid="addFilterNull" onClick={() => add(null)} />
            <button data-testid="addFilterNoCANID" onClick={() => add({})} />
            <button
              data-testid="addFilter"
              onClick={() => add({ can_id: "123", interval: 1000 })}
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <CANFiltersProvider>
            <TestComp />
          </CANFiltersProvider>
        </StatusProvider>
      );
    });

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

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

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

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

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

  describe("updateFilter", () => {
    beforeEach(async () => {
      const TestComp = () => {
        const { busy, updateFilter } = useCANFiltersContext();
        const { message, setMessage } = useStatusContext();
        const update = async (data) => {
          try {
            await updateFilter("TESTVIN1234567890", data.can_id, data);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="error">{message}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <button
              data-testid="updateFilterNull"
              onClick={() => update(null)}
            />
            <button
              data-testid="updateFilterNoCANID"
              onClick={() => update({})}
            />
            <button
              data-testid="updateFilter"
              onClick={() => update({ can_id: "123", interval: 1000 })}
            />
          </>
        );
      };
      render(
        <StatusProvider>
          <CANFiltersProvider>
            <TestComp />
          </CANFiltersProvider>
        </StatusProvider>
      );
    });

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

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

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

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

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

  describe("deleteFilter", () => {
    beforeEach(async () => {
      const TestComp = () => {
        const { busy, deleteFilter } = useCANFiltersContext();
        const { message, setMessage } = useStatusContext();
        const deleteF = async (can_id) => {
          try {
            await deleteFilter("TESTVIN1234567890", can_id);
          } catch (e) {
            setMessage(e.message);
          }
        };

        return (
          <>
            <div data-testid="error">{message}</div>
            <div data-testid="busy">{busy.toString()}</div>
            <button
              data-testid="deleteFilterNull"
              onClick={() => deleteF(null)}
            />
            <button data-testid="deleteFilter" onClick={() => deleteF(123)} />
          </>
        );
      };
      render(
        <StatusProvider>
          <CANFiltersProvider>
            <TestComp />
          </CANFiltersProvider>
        </StatusProvider>
      );
    });

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

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

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

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

const expectedFiltersData = [
  {
    can_id: "123",
    interval: 1000,
  },
  {
    can_id: "456",
    interval: 0,
  },
  {
    can_id: "789-1000",
    interval: 5,
  },
];
