import { focusManager } from "../FocusManager";

const group = {
  current: {
    props: {
      id: "group",
      nextFocusDown: "child1",
      nextFocusUp: "child2",
      nextFocusLeft: "child1",
      nextFocusRight: "child2",
    },
  } as any,
};

const child1 = {
  current: {
    onBlur: jest.fn(),
    props: {
      id: "child1",
      nextFocusDown: "child2",
    },
  } as any,
};

const child2 = {
  current: {
    props: {
      id: "child2",
    },
  } as any,
};

const child3 = {
  current: {
    props: {
      id: "child3",
      disableFocus: true,
    },
  } as any,
};

const child4 = {
  current: {
    props: {
      id: "child4",
      disableFocus: true,
      nextFocusDown: "child2",
    },
  } as any,
};

const child5 = {
  current: {
    props: {
      id: "child5",
      blockFocus: true,
    },
  } as any,
};

jest.useFakeTimers();

describe("FocusManager", () => {
  beforeAll(() => {
    focusManager.registerFocusable(group, { current: null });
    focusManager.registerFocusable(child1, group);
    focusManager.registerFocusable(child2, group);
    focusManager.registerFocusable(child3, child2);

    focusManager.registerFocusable(child4, child2);
    focusManager.registerFocusable(child5, child2);
  });

  it("focusManager should be defined", () => {
    expect(focusManager).toBeDefined();
  });

  it("focusManager isAnyDescendantFocused should be defined", () => {
    expect(focusManager.isAnyDescendantFocused).toBeDefined();
  });

  it("focusManager isAnyDescendantFocused should check if group has child focused", () => {
    /* check state of focus */
    expect(focusManager.isAnyDescendantFocused("group")).toBe(false);

    /* set focus on 2nd child */
    focusManager.setFocus(child2.current.props.id);

    /* check state of focus */
    expect(focusManager.isAnyDescendantFocused("group")).toBe(false);
  });

  it("focusManager isFocusableChildOf should be defined", () => {
    expect(focusManager.isFocusableChildOf).toBeDefined();
  });

  it("focusManager isFocusableChildOf should check if focusable is children of references parent", () => {
    expect(focusManager.isFocusableChildOf(child1, group)).toBeTruthy();
    expect(focusManager.isFocusableChildOf(child2, group)).toBeTruthy();

    expect(
      focusManager.isFocusableChildOf(child2, group, { direct: true })
    ).toBeTruthy();

    expect(
      focusManager.isFocusableChildOf(child3, group, { direct: true })
    ).toBeFalsy();
  });

  it("focusManager setFocus should be defined", () => {
    expect(focusManager.setFocus).toBeDefined();
  });

  it("focusManager setFocus should be defined", () => {
    focusManager.setFocus(child1.current.props.id);
    expect(focusManager.focused?.props.id).toBe("child1");
    expect(focusManager.focusedId).toBe("child1");
    expect(focusManager.isFocused("child1")).toBeTruthy();
  });

  it("focusManager isFocused should be defined", () => {
    focusManager.setFocus(child1.current.props.id);
    expect(focusManager.isFocused("child1")).toBeTruthy();
  });

  it("focusManager resetFocus should be defined", () => {
    expect(focusManager.resetFocus).toBeDefined();
  });

  it("focusManager resetFocus should clear focus", () => {
    focusManager.setFocus(child1.current.props.id);

    expect(focusManager.focusedId).not.toBeNull();

    focusManager.resetFocus();

    expect(focusManager.focusedId).toBeNull();
  });

  it("focusManager onDisableFocusChange should be defined", () => {
    expect(focusManager.onDisableFocusChange).toBeDefined();
  });

  it("focusManager onDisableFocusChange should change focus", () => {
    focusManager.setFocus(child1.current.props.id);
    focusManager.onDisableFocusChange("child1");
    jest.runAllTimers();
    expect(focusManager.focusedId).toBe("child2");
  });

  it("focusManager blurPrevious should be defined", () => {
    expect(focusManager.blurPrevious).toBeDefined();
  });

  it("focusManager blurPrevious should blur focused element", () => {
    child1.current.onBlur.mockClear();
    focusManager.setFocus(child1.current.props.id);
    focusManager.blurPrevious({ direction: "down" });
  });

  it("focusManager - focus shouldn't change if nextFocus has blockFocus", () => {
    focusManager.setFocus(child1.current.props.id);
    focusManager.setFocus(child5.current.props.id);
    expect(focusManager.focusedId).toBe("child1");
  });

  it("focusManager setNextFocus - focus shouldn't change if nextFocus has disableFocus and no options provided", () => {
    focusManager.setFocus(child1.current.props.id);
    expect(focusManager.focusedId).toBe("child1");
    focusManager.setFocus(child3.current.props.id);
    expect(focusManager.focusedId).toBe("child1");
  });

  it("focusManager setNextFocus - focus should change if nextFocus  has disableFocus and has another item in provided direction", () => {
    focusManager.setFocus(child4.current.props.id, { direction: "down" });
    expect(focusManager.focusedId).toBe("child2");
  });

  it("focusManager setNextFocus - focus should change to next item", () => {
    focusManager.setFocus(child1.current.props.id);
    focusManager.setFocus(child2.current.props.id, { direction: "down" });
    expect(focusManager.focusedId).toBe("child2");
  });

  it("focusManager unregisterFocusable should be defined", () => {
    expect(focusManager.unregisterFocusable).toBeDefined();
  });

  it("focusManager unregisterFocusable should unregister", () => {
    focusManager.unregisterFocusable(child5);

    expect(
      focusManager.isFocusableChildOf(child5.current.props.id, child2)
    ).toBeFalsy();
  });

  it("focusManager registerFocusable should be defined", () => {
    expect(focusManager.registerFocusable).toBeDefined();
  });

  it("focusManager registerFocusable should register", () => {
    focusManager.registerFocusable(child5, child2);

    expect(
      focusManager.isFocusableChildOf(child5.current.props.id, child2)
    ).toBeTruthy();
  });

  it("focusManager updateFocusedSilently should be defined", () => {
    expect(focusManager.updateFocusedSilently).toBeDefined();
  });

  it("focusManager updateFocusedSilently should update focus", () => {
    focusManager.updateFocusedSilently(child2);
    expect(focusManager.focusedId).toBe("child2");
  });
});
