import {
  isOptionGroup,
  getOptionLabel,
  getOptionValue,
  filterOptions,
  clamp,
  buildOptionTestId,
} from "./utils";
import type { ComboboxOptions } from "./types";

describe("Combobox utils", () => {
  describe("isOptionGroup", () => {
    it("should return true for valid option groups", () => {
      const options = [
        { heading: "Group 1", options: [{ value: "1", label: "Option 1" }] },
        { heading: "Group 2", options: [{ value: "2", label: "Option 2" }] },
      ];
      expect(isOptionGroup(options)).toBe(true);
    });

    it("should return false for invalid option groups", () => {
      const options = [
        { value: "1", label: "Option 1" },
        { value: "2", label: "Option 2" },
      ];
      expect(isOptionGroup(options)).toBe(false);
    });

    it("should return false for empty options", () => {
      const options = [] as ComboboxOptions<string>;
      expect(isOptionGroup(options)).toBe(false);
    });

    it("should return false for options with missing heading", () => {
      const options = [{ options: [{ value: "1", label: "Option 1" }] }];
      // @ts-expect-error - testing invalid data
      expect(isOptionGroup(options)).toBe(false);
    });

    it("should return false for options with missing options", () => {
      const options = [{ heading: "Group 1" }];
      // @ts-expect-error - testing invalid data
      expect(isOptionGroup(options)).toBe(false);
    });
  });

  describe("getOptionValue", () => {
    it("should return the value for a string option", () => {
      const option = "Option 1";
      expect(getOptionValue(option)).toBe(option);
    });

    it("should return the value for an object option", () => {
      const option = { value: "1", label: "Option 1" };
      expect(getOptionValue(option)).toBe(option.value);
    });
  });

  describe("getOptionLabel", () => {
    it("should return the label for a string option", () => {
      const option = "Option 1";
      expect(getOptionLabel(option)).toBe(option);
    });

    it("should return the label for an object option", () => {
      const option = { value: "1", label: "Option 1" };
      expect(getOptionLabel(option)).toBe(option.label);
    });
  });

  describe("filterOptions", () => {
    it("should filter options based on input value", () => {
      const options = [
        { value: "1", label: "Option 1" },
        { value: "2", label: "Option 2" },
      ];
      const filteredOptions = filterOptions(options, "1");
      expect(filteredOptions).toEqual([{ value: "1", label: "Option 1" }]);
    });

    it("should filter option groups based on input value", () => {
      const options = [
        { heading: "Group 1", options: [{ value: "1", label: "Option 1" }] },
        { heading: "Group 2", options: [{ value: "2", label: "Option 2" }] },
      ];
      const filteredOptions = filterOptions(options, "1");
      expect(filteredOptions).toEqual([
        { heading: "Group 1", options: [{ value: "1", label: "Option 1" }] },
      ]);
    });

    it("should remove groups with no options after filtering", () => {
      const options = [
        { heading: "Group 1", options: [{ value: "1", label: "Option 1" }] },
        { heading: "Group 2", options: [{ value: "2", label: "Option 2" }] },
      ];
      const filteredOptions = filterOptions(options, "3");
      expect(filteredOptions).toEqual([]);
    });

    it("should handle empty options", () => {
      const options = [] as ComboboxOptions<string>;
      const filteredOptions = filterOptions(options, "1");
      expect(filteredOptions).toEqual([]);
    });
  });

  describe("buildOptionTestId", () => {
    it("should build a test ID from prefix and value", () => {
      expect(buildOptionTestId("combobox-option", "Florist")).toBe(
        "combobox-option-florist",
      );
    });

    it("should replace spaces with hyphens", () => {
      expect(buildOptionTestId("trade-option", "Dog Walking")).toBe(
        "trade-option-dog-walking",
      );
    });

    it("should handle multiple consecutive spaces", () => {
      expect(buildOptionTestId("combobox-option", "Foo   Bar")).toBe(
        "combobox-option-foo-bar",
      );
    });

    it("should replace punctuation with hyphens", () => {
      expect(buildOptionTestId("trade-option", "Painter & Decorator")).toBe(
        "trade-option-painter-decorator",
      );
    });

    it("should handle slashes and special characters", () => {
      expect(
        buildOptionTestId("trade-option", "Coffee Shop / Sandwich Bar"),
      ).toBe("trade-option-coffee-shop-sandwich-bar");
    });

    it("should trim leading and trailing whitespace", () => {
      expect(buildOptionTestId("combobox-option", "  Florist  ")).toBe(
        "combobox-option-florist",
      );
    });
  });

  describe("clamp", () => {
    it("should clamp to the min value", () => {
      expect(clamp(-1, 0, 10)).toBe(0);
    });

    it("should clamp to the max value", () => {
      expect(clamp(11, 0, 10)).toBe(10);
    });

    it("should not clamp when within the range", () => {
      expect(clamp(5, 0, 10)).toBe(5);
    });
  });
});
