import {
  isEmptyOrNil,
  ifEmptyUseFallback,
  textTransform,
  getKeyForLabel,
  getLabel,
  getAspectRatio,
  getCellWidth,
  getHeight,
  getLockBadge,
  mapSelfAlignment,
  ifElseFocused,
  castAndFallbackNumber,
  castAndFallbackColor,
  compact,
  getValue,
  getColorFromData,
} from "../";

import {
  entry,
  componentStyle,
  VAL,
  EMPTY,
  FALLBACK,
  TRANSFORM,
} from "./fixtures";

describe("getKeyForLabel", () => {
  it("returns array of strings from data_key when data_key has value", () => {
    expect(getKeyForLabel(VAL.dataKey, VAL.customKey)).toEqual([VAL.dataKey]);

    expect(getKeyForLabel(VAL.dataKeyPath, VAL.customKey)).toEqual(
      VAL.dataKeyArray
    );
  });

  it("returns array of strings from custom_key when data_key equals other", () => {
    expect(getKeyForLabel(VAL.other, VAL.customKey)).toEqual([VAL.customKey]);

    expect(getKeyForLabel(VAL.other, VAL.customKeyPath)).toEqual(
      VAL.customKeyArray
    );
  });

  it("returns empty array when data_key is empty", () => {
    expect(getKeyForLabel(EMPTY.nulls, VAL.customKey)).toEqual([]);
    expect(getKeyForLabel(EMPTY.undef, VAL.customKey)).toEqual([]);
    expect(getKeyForLabel(EMPTY.string, VAL.customKey)).toEqual([""]);
  });

  it("returns empty array when custom_key is empty", () => {
    expect(getKeyForLabel(VAL.other, EMPTY.nulls)).toEqual([]);
    expect(getKeyForLabel(VAL.other, EMPTY.undef)).toEqual([]);
    expect(getKeyForLabel(VAL.other, EMPTY.string)).toEqual([""]);
  });
});

describe("getLabel", () => {
  it("returns a string from dataKey's path", () => {
    expect(getLabel(VAL.dataKey, VAL.customKey)(entry)).toEqual(entry.id);

    expect(getLabel(VAL.dataKeyPath, VAL.customKey)(entry)).toEqual(
      entry.content.src
    );
  });

  it("returns a string from customKey's path", () => {
    expect(getLabel(VAL.other, VAL.customKey)(entry)).toEqual(entry.title);

    expect(getLabel(VAL.other, VAL.customKeyPath)(entry)).toEqual(
      entry.extensions.duration
    );
  });

  it("returns empty string when dataKey is empty or null", () => {
    expect(getLabel(EMPTY.nulls, VAL.customKey)(entry)).toEqual("");
    expect(getLabel(EMPTY.undef, VAL.customKey)(entry)).toEqual("");
    expect(getLabel(EMPTY.string, VAL.customKey)(entry)).toEqual("");
  });

  it("returns empty string when customKey is empty or null", () => {
    expect(getLabel(VAL.other, EMPTY.nulls)(entry)).toEqual("");
    expect(getLabel(VAL.other, EMPTY.undef)(entry)).toEqual("");
    expect(getLabel(VAL.other, EMPTY.string)(entry)).toEqual("");
  });

  it("returns customKey or dataKey field when an entry is empty or null", () => {
    expect(getLabel(VAL.other, VAL.customKey)(EMPTY.object)).toEqual(
      VAL.customKey
    );

    expect(getLabel(VAL.dataKey, VAL.customKey)(EMPTY.nulls)).toEqual(
      VAL.dataKey
    );

    expect(getLabel(VAL.other, VAL.customKey)(EMPTY.undef)).toEqual(
      VAL.customKey
    );
  });
});

describe("getAspectRatio", () => {
  it("returns a number for one of the predefined keys", () => {
    expect(getAspectRatio(VAL.aspect)).toEqual(VAL.aspectResult);
    expect(getAspectRatio(VAL.aspectColon)).toEqual(VAL.aspectResult);
  });

  it("returns a number for the custom entered key, when user selects other", () => {
    expect(getAspectRatio(VAL.other, VAL.aspect)).toEqual(VAL.aspectResult);
  });

  it("returns a number with the default aspect ratio when keys are not valid", () => {
    expect(getAspectRatio(VAL.string)).toEqual(VAL.defaultAspectResult);
    expect(getAspectRatio(EMPTY.nulls)).toEqual(VAL.defaultAspectResult);

    expect(getAspectRatio(VAL.other, VAL.string)).toEqual(
      VAL.defaultAspectResult
    );
  });
});

describe("isEmptyOrNil", () => {
  it("returns true if give value is empty", () => {
    expect(isEmptyOrNil(EMPTY.array)).toBe(true);
    expect(isEmptyOrNil(EMPTY.object)).toBe(true);
    expect(isEmptyOrNil(EMPTY.string)).toBe(true);
    expect(isEmptyOrNil(EMPTY.nulls)).toBe(true);
    expect(isEmptyOrNil(EMPTY.nan)).toBe(true);
  });

  it("returns false if give value has a non empty value", () => {
    expect(isEmptyOrNil(VAL.array)).toBe(false);
    expect(isEmptyOrNil(VAL.object)).toBe(false);
    expect(isEmptyOrNil(VAL.string)).toBe(false);
    expect(isEmptyOrNil(VAL.boolT)).toBe(false);
    expect(isEmptyOrNil(VAL.boolF)).toBe(false);
    expect(isEmptyOrNil(VAL.zero)).toBe(false);
    expect(isEmptyOrNil(EMPTY.nullNum)).toBe(false);
  });
});

describe("ifEmptyUseFallback", () => {
  it("returns the fallback value if empty", () => {
    expect(ifEmptyUseFallback(EMPTY.array, FALLBACK)).toBe(FALLBACK);
    expect(ifEmptyUseFallback(EMPTY.object, FALLBACK)).toBe(FALLBACK);
    expect(ifEmptyUseFallback(EMPTY.string, FALLBACK)).toBe(FALLBACK);
    expect(ifEmptyUseFallback(EMPTY.nulls, FALLBACK)).toBe(FALLBACK);
  });

  it("returns the original value if it has a non empty value", () => {
    expect(ifEmptyUseFallback(VAL.array, FALLBACK)).toBe(VAL.array);
    expect(ifEmptyUseFallback(VAL.object, FALLBACK)).toBe(VAL.object);
    expect(ifEmptyUseFallback(VAL.string, FALLBACK)).toBe(VAL.string);
    expect(ifEmptyUseFallback(VAL.boolT, FALLBACK)).toBe(VAL.boolT);
    expect(ifEmptyUseFallback(VAL.boolF, FALLBACK)).toBe(VAL.boolF);
    expect(ifEmptyUseFallback(VAL.zero, FALLBACK)).toBe(VAL.zero);
  });
});

describe("textTransform", () => {
  it("returns the original string if default, null, or undefined", () => {
    expect(textTransform(TRANSFORM.default, VAL.string)).toBe(VAL.string);
    expect(textTransform(EMPTY.nulls, VAL.string)).toBe(VAL.string);
    expect(textTransform(EMPTY.string, VAL.string)).toBe(VAL.string);
    expect(textTransform(EMPTY.undef, VAL.string)).toBe(VAL.string);
  });

  it("returns the transformed value of a string", () => {
    expect(textTransform(TRANSFORM.default, VAL.string)).toBe(VAL.string);
    expect(textTransform(TRANSFORM.capitalize, VAL.string)).toBe(VAL.capital);
    expect(textTransform(TRANSFORM.lowercase, VAL.string)).toBe(VAL.lower);
    expect(textTransform(TRANSFORM.uppercase, VAL.string)).toBe(VAL.upper);

    expect(textTransform(TRANSFORM.capitalize, VAL.sentence)).toBe(
      VAL.capitalSentence
    );
  });
});

describe("getCellWidth", () => {
  it("returns the cell width defined at the component level", () => {
    expect(getCellWidth(componentStyle)).toBe(VAL.width);
  });

  it("returns default cell width when could not find cell_width key", () => {
    expect(getCellWidth(VAL.object)).toBe(VAL.defaultWidth);
  });
});

describe("getHeight", () => {
  it("returns the height of the cell using aspect ratio and cell width", () => {
    expect(getHeight(VAL.width, VAL.defaultAspectResult)).toBe(VAL.height);
  });

  it("returns default cell height if the height could not be calculated", () => {
    expect(getHeight(VAL.width, VAL.aspect)).toBe(VAL.defaultHeight);
  });
});

describe("getLockBadge", () => {
  const config = {
    locked_badge: "http://img.com/locked_badge.png",
    unlocked_badge: "http://img.com/unlocked_badge.png",
  };

  const getConfigValue = (key) => config[key];

  it("returns null when the lock badge data key doesn't exist in the data", () => {
    const data = {
      extensions: {
        prop: "value",
      },
    };

    const lockBadgeDataKey = "extensions.free";

    expect(getLockBadge(getConfigValue, data, lockBadgeDataKey)).toBe(null);
  });

  it("doesn't throw if the lock badge data key in the config is null", () => {
    const data = {
      extensions: {
        free: null,
      },
    };

    const lockBadgeDataKey = null;

    expect(() =>
      getLockBadge(getConfigValue, data, lockBadgeDataKey)
    ).not.toThrow();
  });

  it("returns null if the lock badge data key in the config is null", () => {
    const data = {
      extensions: {
        free: null,
      },
    };

    const lockBadgeDataKey = null;

    expect(getLockBadge(getConfigValue, data, lockBadgeDataKey)).toBe(null);
  });

  it("returns the value of the **unlocked** badge if the data key exists and has a truthy value", () => {
    const data = {
      extensions: {
        unlocked: true,
        unlocked_string: "true",
      },
    };

    const lockBadgeDataKey = "extensions.unlocked";

    expect(getLockBadge(getConfigValue, data, lockBadgeDataKey)).toBe(
      getConfigValue("unlocked_badge")
    );

    const lockBadgeDataKeyString = "extensions.unlocked_string";

    expect(getLockBadge(getConfigValue, data, lockBadgeDataKeyString)).toBe(
      getConfigValue("unlocked_badge")
    );
  });

  it("returns the value of the **locked** badge if the data key exists and has a truthy value", () => {
    const data = {
      extensions: {
        unlocked: false,
        unlocked_string: "false",
      },
    };

    const lockBadgeDataKey = "extensions.unlocked";

    expect(getLockBadge(getConfigValue, data, lockBadgeDataKey)).toBe(
      getConfigValue("locked_badge")
    );

    const lockBadgeDataKeyString = "extensions.unlocked_string";

    expect(getLockBadge(getConfigValue, data, lockBadgeDataKeyString)).toBe(
      getConfigValue("locked_badge")
    );
  });

  describe("mapSelfAlignment", () => {
    it("should have mapSelfAlignment", () => {
      expect(mapSelfAlignment).toBeDefined();
      expect(mapSelfAlignment("left")).toBe("flex-start");
      expect(mapSelfAlignment("right")).toBe("flex-end");
      expect(mapSelfAlignment("center")).toBe("center");
      expect(mapSelfAlignment("middle")).toBe("center");
      expect(mapSelfAlignment("anything")).toBe("flex-start");
    });
  });
});

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

  it("returns first value when state is focused", () => {
    expect(ifElseFocused("focused", "foo", "bar")).toEqual("foo");
  });

  it("returns second value if state isn't focused", () => {
    expect(ifElseFocused("selected", "foo", "bar")).toEqual("bar");
    expect(ifElseFocused("test", "foo", "bar")).toEqual("bar");
    expect(ifElseFocused(undefined, "foo", "bar")).toEqual("bar");
    expect(ifElseFocused(null, "foo", "bar")).toEqual("bar");
  });
});

describe("castAndFallbackColor", () => {
  it("fixes the color if passed", () => {
    const currentResult = castAndFallbackColor("#00111111");
    const expectedResult = "#11111100";
    expect(currentResult).toEqual(expectedResult);
  });

  it("falls back to white if color not defined", () => {
    const currentResult = castAndFallbackColor(undefined);
    expect(currentResult).toEqual("#FFFFFF");
  });
});

describe("castAndFallbackNumber", () => {
  beforeEach(() => {
    jest.resetModules();
  });

  it("fixes the color if passed", () => {
    const currentResult = castAndFallbackNumber("1");
    const expectedResult = 1;
    expect(currentResult).toEqual(expectedResult);
  });

  it("falls back to white if color not defined", () => {
    const currentResult = castAndFallbackNumber(undefined);
    expect(currentResult).toEqual(0);
  });
});

it("should have castAndFallbackNumber working properly", () => {
  expect(castAndFallbackNumber).toBeDefined();
  expect(castAndFallbackNumber(2)).toBe(2);
  expect(castAndFallbackNumber("1")).toBe(1);
  expect(castAndFallbackNumber(0)).toBe(0);
  expect(castAndFallbackNumber("")).toBe(0);
  expect(castAndFallbackNumber(null)).toBe(0);
  expect(castAndFallbackNumber(undefined)).toBe(0);
  expect(castAndFallbackNumber("undefined")).toBe(0);
  expect(castAndFallbackNumber(NaN)).toBe(0);
});

it("should have castAndFallbackColor", () => {
  expect(castAndFallbackColor).toBeDefined();
  expect(castAndFallbackColor("#000")).toBe("#000");

  expect(castAndFallbackColor("rgba(255,255,255,0)")).toBe(
    "rgba(255,255,255,0)"
  );

  expect(castAndFallbackColor(null)).toBe("#FFFFFF");
  expect(castAndFallbackColor(undefined)).toBe("#FFFFFF");
});

it("should have compact", () => {
  expect(compact).toBeDefined();
  expect(compact([1, 2, 3, null, 5])).toStrictEqual([1, 2, 3, 5]);
});

describe("getValue", () => {
  it("should have getValue", () => {
    expect(getValue).toBeDefined();
  });

  it("should return value if it is not null", () => {
    const value = "value";
    expect(getValue({ value })(value)).toEqual(value);
  });

  it("should return null when no value", () => {
    const value = "value2";
    expect(getValue({ value })(value)).toBeNull();
  });
});

describe("getColorFromData", () => {
  it("should return value directly if valid color", () => {
    const result = getColorFromData({
      data: entry,
      valueFromLayout: "#123456",
    });

    expect(result).toBe("#123456");
  });

  it("should return valid rgba color name", () => {
    const result = getColorFromData({
      data: entry,
      valueFromLayout: "rgba(255, 255, 255, 0)",
    });

    expect(result).toBe("rgba(255, 255, 255, 0)");
  });

  it("should return path value if path value is set and is valid color", () => {
    const result = getColorFromData({
      data: entry,
      valueFromLayout: "background_color",
    });

    expect(result).toBe("#123456");
  });

  it("should return path value if path value is set, nested and is valid color", () => {
    const result = getColorFromData({
      data: entry,
      valueFromLayout: "extensions.background_color",
    });

    expect(result).toBe("#123456");
  });

  it("should return valueFromLayout if path does not exist", () => {
    const result = getColorFromData({
      data: entry,
      valueFromLayout: "invalid_color",
    });

    expect(result).toBe("invalid_color");
  });
});
