/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

import {
  WIDGET_REGISTRY,
  getWidgetOrder,
  isWidgetAddable,
  isWidgetEnabled,
  isWidgetToggleVisible,
  isWidgetsContainerVisible,
  resolveWidgetSize,
  resolveWidgetOrder,
  resolveWidgetHasSidebar,
  PREF_WIDGETS_ORDER,
} from "common/WidgetsRegistry.mjs";

const registryIds = WIDGET_REGISTRY.map(w => w.id);
const listsWidget = WIDGET_REGISTRY.find(w => w.id === "lists");

describe("isWidgetToggleVisible", () => {
  it("is false when nothing enables it", () => {
    expect(isWidgetToggleVisible(listsWidget, {})).toBe(false);
  });

  it("is true via the system pref", () => {
    expect(
      isWidgetToggleVisible(listsWidget, {
        "widgets.system.lists.enabled": true,
      })
    ).toBe(true);
  });

  it("is true via trainhopConfig.widgets (addable)", () => {
    expect(
      isWidgetToggleVisible(listsWidget, {
        trainhopConfig: { widgets: { listsEnabled: true } },
      })
    ).toBe(true);
  });

  it("is true via the widgetsSettings.*Visible override", () => {
    expect(
      isWidgetToggleVisible(listsWidget, {
        trainhopConfig: { widgetsSettings: { listsVisible: true } },
      })
    ).toBe(true);
  });

  it("is additive only — a false widgetsSettings value cannot hide a system-enabled toggle", () => {
    expect(
      isWidgetToggleVisible(listsWidget, {
        "widgets.system.lists.enabled": true,
        trainhopConfig: { widgetsSettings: { listsVisible: false } },
      })
    ).toBe(true);
  });
});

describe("isWidgetsContainerVisible", () => {
  it("is false when nothing enables it", () => {
    expect(isWidgetsContainerVisible({})).toBe(false);
  });

  it("is true via the system pref", () => {
    expect(isWidgetsContainerVisible({ "widgets.system.enabled": true })).toBe(
      true
    );
  });

  it("is true via trainhopConfig.widgets.enabled", () => {
    expect(
      isWidgetsContainerVisible({
        trainhopConfig: { widgets: { enabled: true } },
      })
    ).toBe(true);
  });

  it("is true via widgetsSettings.enabled", () => {
    expect(
      isWidgetsContainerVisible({
        trainhopConfig: { widgetsSettings: { enabled: true } },
      })
    ).toBe(true);
  });
});

describe("getWidgetOrder", () => {
  it("returns registry default order when pref is empty", () => {
    expect(getWidgetOrder("")).toEqual(registryIds);
  });

  it("returns registry default order when pref is null/undefined", () => {
    expect(getWidgetOrder(null)).toEqual(registryIds);
    expect(getWidgetOrder(undefined)).toEqual(registryIds);
  });

  it("respects a fully-specified custom order", () => {
    expect(
      getWidgetOrder(
        "focusTimer,lists,weather,sportsWidget,clocks,privacy,crossword"
      )
    ).toEqual([
      "focusTimer",
      "lists",
      "weather",
      "sportsWidget",
      "clocks",
      "privacy",
      "crossword",
      "stocks",
      "pictureOfTheDay",
    ]);
  });

  it("appends missing registry IDs after saved ones", () => {
    expect(getWidgetOrder("weather")).toEqual([
      "weather",
      "sportsWidget",
      "clocks",
      "lists",
      "focusTimer",
      "privacy",
      "crossword",
      "stocks",
      "pictureOfTheDay",
    ]);
  });

  it("filters out unknown IDs from the saved pref", () => {
    expect(getWidgetOrder("unknownWidget,lists,weather")).toEqual([
      "lists",
      "weather",
      "sportsWidget",
      "clocks",
      "focusTimer",
      "privacy",
      "crossword",
      "stocks",
      "pictureOfTheDay",
    ]);
  });

  it("handles partial order with only one ID", () => {
    const result = getWidgetOrder("focusTimer");
    expect(result[0]).toBe("focusTimer");
    expect(result.length).toBe(registryIds.length);
  });

  it("deduplicates repeated IDs in the saved pref", () => {
    const result = getWidgetOrder("focusTimer,focusTimer,lists");
    expect(result).toEqual([
      "focusTimer",
      "lists",
      "sportsWidget",
      "clocks",
      "weather",
      "privacy",
      "crossword",
      "stocks",
      "pictureOfTheDay",
    ]);
    expect(result.length).toBe(registryIds.length);
  });
});

describe("resolveWidgetOrder", () => {
  it("returns registry order when no user or trainhop order is set", () => {
    expect(resolveWidgetOrder({ [PREF_WIDGETS_ORDER]: "" })).toEqual(
      registryIds
    );
  });

  it("uses the user-saved order when set", () => {
    expect(
      resolveWidgetOrder({ [PREF_WIDGETS_ORDER]: "weather,lists,focusTimer" })
    ).toEqual([
      "weather",
      "lists",
      "focusTimer",
      "sportsWidget",
      "clocks",
      "privacy",
      "crossword",
      "stocks",
      "pictureOfTheDay",
    ]);
  });

  it("uses trainhop order when no user order is saved", () => {
    expect(
      resolveWidgetOrder({
        [PREF_WIDGETS_ORDER]: "",
        trainhopConfig: { widgets: { order: "focusTimer,weather,lists" } },
      })
    ).toEqual([
      "focusTimer",
      "weather",
      "lists",
      "sportsWidget",
      "clocks",
      "privacy",
      "crossword",
      "stocks",
      "pictureOfTheDay",
    ]);
  });

  it("user order takes precedence over trainhop order", () => {
    expect(
      resolveWidgetOrder({
        [PREF_WIDGETS_ORDER]: "lists,focusTimer,weather",
        trainhopConfig: { widgets: { order: "weather,lists,focusTimer" } },
      })
    ).toEqual([
      "lists",
      "focusTimer",
      "weather",
      "sportsWidget",
      "clocks",
      "privacy",
      "crossword",
      "stocks",
      "pictureOfTheDay",
    ]);
  });
});

describe("isWidgetAddable", () => {
  const listsWidget = WIDGET_REGISTRY.find(w => w.id === "lists");

  it("returns true when system pref is set", () => {
    expect(
      isWidgetAddable(listsWidget, {
        [listsWidget.systemEnabledPref]: true,
      })
    ).toBe(true);
  });

  it("returns true when trainhop overrides the system gate", () => {
    expect(
      isWidgetAddable(listsWidget, {
        [listsWidget.systemEnabledPref]: false,
        trainhopConfig: {
          widgets: { [listsWidget.trainhopEnabledKey]: true },
        },
      })
    ).toBe(true);
  });

  it("returns false when neither system nor trainhop are set", () => {
    expect(
      isWidgetAddable(listsWidget, {
        [listsWidget.systemEnabledPref]: false,
      })
    ).toBe(false);
  });

  it("is addable when revealed via widgetsSettings (so the toggle is functional)", () => {
    expect(
      isWidgetAddable(listsWidget, {
        [listsWidget.systemEnabledPref]: false,
        trainhopConfig: { widgetsSettings: { listsVisible: true } },
      })
    ).toBe(true);
  });

  it("does not consider the user's enabled pref", () => {
    expect(
      isWidgetAddable(listsWidget, {
        [listsWidget.systemEnabledPref]: true,
        [listsWidget.enabledPref]: false,
      })
    ).toBe(true);
  });
});

describe("isWidgetEnabled", () => {
  const listsWidget = WIDGET_REGISTRY.find(w => w.id === "lists");

  it("returns false when widgetsEnabled is false", () => {
    expect(
      isWidgetEnabled(
        listsWidget,
        {
          [listsWidget.enabledPref]: true,
          [listsWidget.systemEnabledPref]: true,
        },
        false
      )
    ).toBe(false);
  });

  it("returns true when system pref and user pref are both set", () => {
    expect(
      isWidgetEnabled(
        listsWidget,
        {
          [listsWidget.enabledPref]: true,
          [listsWidget.systemEnabledPref]: true,
        },
        true
      )
    ).toBe(true);
  });

  it("returns false when system pref is not set and no trainhop", () => {
    expect(
      isWidgetEnabled(
        listsWidget,
        {
          [listsWidget.enabledPref]: true,
          [listsWidget.systemEnabledPref]: false,
        },
        true
      )
    ).toBe(false);
  });

  it("returns false when user pref is disabled even if system is set", () => {
    expect(
      isWidgetEnabled(
        listsWidget,
        {
          [listsWidget.enabledPref]: false,
          [listsWidget.systemEnabledPref]: true,
        },
        true
      )
    ).toBe(false);
  });

  it("returns true when trainhop overrides the system gate", () => {
    expect(
      isWidgetEnabled(
        listsWidget,
        {
          [listsWidget.enabledPref]: true,
          [listsWidget.systemEnabledPref]: false,
          trainhopConfig: {
            widgets: { [listsWidget.trainhopEnabledKey]: true },
          },
        },
        true
      )
    ).toBe(true);
  });
});

describe("resolveWidgetSize", () => {
  const weatherWidget = WIDGET_REGISTRY.find(w => w.id === "weather");

  it("returns the user-set pref when non-empty", () => {
    expect(
      resolveWidgetSize(weatherWidget, { [weatherWidget.sizePref]: "large" })
    ).toBe("large");
  });

  it("falls back to defaultSize when pref is empty and no trainhop", () => {
    expect(
      resolveWidgetSize(weatherWidget, { [weatherWidget.sizePref]: "" })
    ).toBe(weatherWidget.defaultSize);
  });

  it("uses trainhop size suggestion when pref is empty", () => {
    expect(
      resolveWidgetSize(weatherWidget, {
        [weatherWidget.sizePref]: "",
        trainhopConfig: {
          widgets: { [weatherWidget.trainhopSizeKey]: "small" },
        },
      })
    ).toBe("small");
  });

  it("user pref wins over trainhop suggestion", () => {
    expect(
      resolveWidgetSize(weatherWidget, {
        [weatherWidget.sizePref]: "medium",
        trainhopConfig: {
          widgets: { [weatherWidget.trainhopSizeKey]: "large" },
        },
      })
    ).toBe("medium");
  });
});

describe("resolveWidgetHasSidebar", () => {
  const weatherWidget = WIDGET_REGISTRY.find(w => w.id === "weather");
  const listsWidget = WIDGET_REGISTRY.find(w => w.id === "lists");

  it("returns the static hasSidebar value when no trainhop key is defined", () => {
    expect(resolveWidgetHasSidebar(listsWidget, {})).toBe(false);
  });

  it("returns the static hasSidebar value when trainhop key is not present in prefs", () => {
    expect(resolveWidgetHasSidebar(weatherWidget, {})).toBe(true);
  });

  it("returns the trainhop override when present", () => {
    expect(
      resolveWidgetHasSidebar(weatherWidget, {
        trainhopConfig: {
          widgets: { [weatherWidget.trainhopSidebarKey]: false },
        },
      })
    ).toBe(false);
  });

  it("trainhop can force sidebar on for a widget with hasSidebar: true", () => {
    expect(
      resolveWidgetHasSidebar(weatherWidget, {
        trainhopConfig: {
          widgets: { [weatherWidget.trainhopSidebarKey]: true },
        },
      })
    ).toBe(true);
  });
});
