import CommonUtil from "./CommonUtil";

describe("Util", () => {
  describe("query", () => {
    test("returns empty string when empty object is passed", () => {
      const result = CommonUtil.query({});
      expect(result).toEqual("");
    });

    test("returns encoded query string with single key-value pair", () => {
      const result = CommonUtil.query({ key: "value" });
      expect(result).toEqual("key=value");
    });

    test("returns encoded query string with multiple key-value pairs", () => {
      const result = CommonUtil.query({ key1: "value1", key2: "value2" });
      expect(result).toEqual("key1=value1&key2=value2");
    });

    test("ignores key-value pairs with undefined values", () => {
      const result = CommonUtil.query({ key1: "value1", key2: undefined });
      expect(result).toEqual("key1=value1");
    });

    test("ignores key-value pairs with undefined keys", () => {
      const result = CommonUtil.query({ key1: "value1", undefined: "value2" });
      expect(result).toEqual("key1=value1");
    });
  });

  describe("sleep", () => {
    test("resolves after the specified time", async () => {
      const start = new Date();
      await CommonUtil.sleep(200);
      const end = new Date();
      const duration = end.getTime() - start.getTime();
      expect(duration).toBeGreaterThan(198);
    });
  });

  describe("sleepRandom", () => {
    test("resolves after the specified time", async () => {
      const start = new Date();
      await CommonUtil.sleepRandom(300);
      const end = new Date();
      const duration = end.getTime() - start.getTime();
      expect(duration).toBeGreaterThan(299);
    });
  });

  describe("minutesToMs", () => {
    test("converts minutes to milliseconds", () => {
      const result = CommonUtil.minutesToMs(2);
      expect(result).toEqual(120000);
    });
  });

  describe("between", () => {
    test("returns true when number is between min and max", () => {
      expect(CommonUtil.between(5, 1, 10)).toBe(true);
    });

    test("returns true when number equals min", () => {
      expect(CommonUtil.between(5, 5, 10)).toBe(true);
    });

    test("returns true when number equals max", () => {
      expect(CommonUtil.between(10, 5, 10)).toBe(true);
    });

    test("returns false when number is below min", () => {
      expect(CommonUtil.between(1, 5, 10)).toBe(false);
    });

    test("returns false when number is above max", () => {
      expect(CommonUtil.between(15, 5, 10)).toBe(false);
    });
  });

  describe("futureDateByHours", () => {
    test("returns date in future by specified hours", () => {
      const now = new Date();
      const futureDate = CommonUtil.futureDateByHours(2);

      expect(futureDate.getTime()).toBeGreaterThan(now.getTime());
      expect(futureDate.getHours()).toBe((now.getHours() + 2) % 24);
    });
  });

  describe("futureDateByMinutes", () => {
    test("returns date in future by specified minutes", () => {
      const now = new Date();
      const futureDate = CommonUtil.futureDateByMinutes(30);

      expect(futureDate.getTime()).toBeGreaterThan(now.getTime());
      const expectedTime = now.getTime() + (30 + 1) * 60 * 1000; // +1 for padding
      expect(Math.abs(futureDate.getTime() - expectedTime)).toBeLessThan(1000);
    });
  });

  describe("futureDateBySeconds", () => {
    test("returns date in future by specified seconds", () => {
      const now = new Date();
      const futureDate = CommonUtil.futureDateBySeconds(30);

      expect(futureDate.getTime()).toBeGreaterThan(now.getTime());
      const expectedTime = now.getTime() + 30 * 1000;
      expect(Math.abs(futureDate.getTime() - expectedTime)).toBeLessThan(1000);
    });
  });

  describe("hoursToMinutes", () => {
    test("converts hours to minutes", () => {
      expect(CommonUtil.hoursToMinutes(2)).toBe(120);
      expect(CommonUtil.hoursToMinutes(0.5)).toBe(30);
      expect(CommonUtil.hoursToMinutes(24)).toBe(1440);
    });
  });

  describe("minutesToSeconds", () => {
    test("converts minutes to seconds", () => {
      expect(CommonUtil.minutesToSeconds(1)).toBe(60);
      expect(CommonUtil.minutesToSeconds(5)).toBe(300);
      expect(CommonUtil.minutesToSeconds(0.5)).toBe(30);
    });
  });

  describe("random", () => {
    test("returns number within specified range", () => {
      for (let i = 0; i < 100; i++) {
        const result = CommonUtil.random(1, 10);
        expect(result).toBeGreaterThanOrEqual(1);
        expect(result).toBeLessThanOrEqual(10);
      }
    });

    test("returns single number when min equals max", () => {
      expect(CommonUtil.random(5, 5)).toBe(5);
    });

    test("handles negative numbers", () => {
      for (let i = 0; i < 10; i++) {
        const result = CommonUtil.random(-10, -1);
        expect(result).toBeGreaterThanOrEqual(-10);
        expect(result).toBeLessThanOrEqual(-1);
      }
    });
  });

  describe("query - extended tests", () => {
    test("handles boolean values", () => {
      const result = CommonUtil.query({ active: true, visible: false });
      expect(result).toBe("active=true&visible=false");
    });

    test("handles number values", () => {
      const result = CommonUtil.query({ count: 42, rating: 4.5 });
      expect(result).toBe("count=42&rating=4.5");
    });

    test("handles special characters in values", () => {
      const result = CommonUtil.query({
        search: "hello world",
        tag: "tag&value",
      });
      expect(result).toBe("search=hello%20world&tag=tag%26value");
    });

    test("ignores keys that are string 'undefined'", () => {
      const result = CommonUtil.query({ key1: "value1", undefined: "value2" });
      expect(result).toBe("key1=value1");
    });

    test("handles zero values", () => {
      const result = CommonUtil.query({ count: 0, rating: 0 });
      expect(result).toBe("count=0&rating=0");
    });
  });
});
