/* Any copyright is dedicated to the Public Domain.
   https://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

Services.scriptloader.loadSubScript(
  new URL("head_smart_window.js", gTestPath).href,
  this
);

requestLongerTimeout(3);
describe("Smart Window model settings", () => {
  let doc, win;

  beforeEach(async function setup() {
    await SpecialPowers.pushPrefEnv({
      set: [
        ["browser.preferences.aiControls", true],
        ["browser.smartwindow.enabled", true],
        ["browser.smartwindow.tos.consentTime", 1770830464],
      ],
    });
  });

  afterEach(async () => {
    BrowserTestUtils.removeTab(gBrowser.selectedTab);

    // Clean up prefs that changed by tests
    Services.prefs.clearUserPref("browser.smartwindow.apiKey");
    Services.prefs.clearUserPref("browser.smartwindow.endpoint");
    Services.prefs.clearUserPref("browser.smartwindow.firstrun.modelChoice");
    Services.prefs.clearUserPref("browser.smartwindow.model");
    Services.prefs.clearUserPref("browser.smartwindow.preferences.endpoint");

    await SpecialPowers.popPrefEnv();
  });

  it("shows model selection when AI Window is enabled", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const modelSelection = doc.getElementById("modelSelection");
    Assert.ok(modelSelection, "Model selection exists");
    Assert.ok(
      BrowserTestUtils.isVisible(modelSelection),
      "Model selection is visible"
    );
  });

  it("selects no model on initial load if user didn't select from onboarding", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const modelSelection = doc.getElementById("modelSelection");
    Assert.equal(
      modelSelection.value,
      null,
      "No radio is selected if user didn't select model choices"
    );
  });

  it("selects model from onboarding choice", async () => {
    await SpecialPowers.pushPrefEnv({
      set: [["browser.smartwindow.firstrun.modelChoice", "2"]],
    });

    ({ doc, win } = await openSmartWindowPanel());

    const modelSelection = doc.getElementById("modelSelection");
    Assert.equal(
      modelSelection.value,
      "2",
      "Model from onboarding choice is selected"
    );
  });

  it("saves preset model immediately when selected", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const fastRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-fast"]'
    );

    await BrowserTestUtils.waitForCondition(
      () => BrowserTestUtils.isVisible(fastRadio),
      "Waiting for radio to be visible"
    );

    fastRadio.click();

    await BrowserTestUtils.waitForCondition(
      () =>
        Services.prefs.getStringPref(
          "browser.smartwindow.firstrun.modelChoice",
          ""
        ) === "1",
      "Waiting for model pref to be saved"
    );

    Assert.equal(
      Services.prefs.getStringPref("browser.smartwindow.firstrun.modelChoice"),
      "1",
      "Model pref is saved immediately"
    );

    fastRadio.focus();
    EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        Services.prefs.getStringPref(
          "browser.smartwindow.firstrun.modelChoice",
          ""
        ) === "2",
      "Waiting for model pref to be saved via keyboard"
    );

    Assert.equal(
      Services.prefs.getStringPref("browser.smartwindow.firstrun.modelChoice"),
      "2",
      "Model pref is saved via keyboard"
    );
  });

  it("shows custom fields when custom radio is selected", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const customRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-custom"]'
    );
    EventUtils.synthesizeMouseAtCenter(customRadio, {}, win);

    await BrowserTestUtils.waitForCondition(
      () => BrowserTestUtils.isVisible(doc.getElementById("customModelName")),
      "Waiting for custom fields to be visible"
    );

    const customModelName = doc.getElementById("customModelName");
    const customModelEndpoint = doc.getElementById("customModelEndpoint");
    const customModelAuthToken = doc.getElementById("customModelAuthToken");
    const customModelSaveButton = doc.getElementById("customModelSaveButton");

    Assert.ok(
      BrowserTestUtils.isVisible(customModelName),
      "Custom model name input is visible"
    );
    Assert.ok(
      BrowserTestUtils.isVisible(customModelEndpoint),
      "Custom model endpoint input is visible"
    );
    Assert.ok(
      BrowserTestUtils.isVisible(customModelAuthToken),
      "Custom model auth token input is visible"
    );
    Assert.ok(
      BrowserTestUtils.isVisible(customModelSaveButton),
      "Custom model save button is visible"
    );

    // Reset to preset for keyboard test
    const fastRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-fast"]'
    );
    fastRadio.click();

    await BrowserTestUtils.waitForCondition(
      () => !BrowserTestUtils.isVisible(doc.getElementById("customModelName")),
      "Waiting for custom fields to be hidden"
    );

    fastRadio.focus();
    // Arrow down 3 times to get to custom (All-purpose -> Fast -> Personalization -> Custom)
    EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
    EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
    EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);

    await BrowserTestUtils.waitForCondition(
      () => BrowserTestUtils.isVisible(doc.getElementById("customModelName")),
      "Waiting for custom fields to be visible via keyboard"
    );

    Assert.ok(
      BrowserTestUtils.isVisible(doc.getElementById("customModelName")),
      "Custom model name input is visible via keyboard"
    );
  });

  it("save button is disabled when endpoint is empty", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const customRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-custom"]'
    );
    EventUtils.synthesizeMouseAtCenter(customRadio, {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        BrowserTestUtils.isVisible(doc.getElementById("customModelSaveButton")),
      "Waiting for save button to be visible"
    );

    const customModelSaveButton = doc.getElementById("customModelSaveButton");
    Assert.ok(
      customModelSaveButton.disabled,
      "Save button is disabled when endpoint is empty"
    );
  });

  it("disables save button when endpoint URL is invalid", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const customRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-custom"]'
    );
    EventUtils.synthesizeMouseAtCenter(customRadio, {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        BrowserTestUtils.isVisible(doc.getElementById("customModelEndpoint")),
      "Waiting for custom fields to be visible"
    );

    const customModelEndpoint = doc.getElementById("customModelEndpoint");
    customModelEndpoint.value = "example.com";
    customModelEndpoint.dispatchEvent(new Event("input"));

    await BrowserTestUtils.waitForCondition(
      () => doc.getElementById("customModelSaveButton").disabled,
      "Waiting for save button to be disabled because endpoint is not valid"
    );

    const customModelSaveButton = doc.getElementById("customModelSaveButton");
    Assert.ok(
      customModelSaveButton.disabled,
      "Save button is disabled when URL is not HTTPS or localhost"
    );
  });

  it("enables save button when endpoint URL is valid HTTPS", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const customRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-custom"]'
    );
    EventUtils.synthesizeMouseAtCenter(customRadio, {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        BrowserTestUtils.isVisible(doc.getElementById("customModelEndpoint")),
      "Waiting for custom fields to be visible"
    );

    const customModelEndpoint = doc.getElementById("customModelEndpoint");
    customModelEndpoint.value = "https://example.com";
    customModelEndpoint.dispatchEvent(new Event("change", { bubbles: true }));

    await BrowserTestUtils.waitForCondition(
      () => !doc.getElementById("customModelSaveButton").disabled,
      "Waiting for save button to be enabled"
    );

    const customModelSaveButton = doc.getElementById("customModelSaveButton");
    Assert.ok(
      !customModelSaveButton.disabled,
      "Save button is enabled when URL is HTTPS"
    );
  });

  it("enables save button when endpoint URL is localhost", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const customRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-custom"]'
    );
    EventUtils.synthesizeMouseAtCenter(customRadio, {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        BrowserTestUtils.isVisible(doc.getElementById("customModelEndpoint")),
      "Waiting for custom fields to be visible"
    );

    const customModelEndpoint = doc.getElementById("customModelEndpoint");
    customModelEndpoint.value = "http://localhost:8080";
    customModelEndpoint.dispatchEvent(new Event("change", { bubbles: true }));

    await BrowserTestUtils.waitForCondition(
      () => !doc.getElementById("customModelSaveButton").disabled,
      "Waiting for save button to be enabled"
    );

    const customModelSaveButton = doc.getElementById("customModelSaveButton");
    Assert.ok(
      !customModelSaveButton.disabled,
      "Save button is enabled when URL is localhost"
    );
  });

  it("saves custom model when save button is clicked", async () => {
    ({ doc, win } = await openSmartWindowPanel());

    const customRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-custom"]'
    );
    EventUtils.synthesizeMouseAtCenter(customRadio, {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        BrowserTestUtils.isVisible(doc.getElementById("customModelEndpoint")),
      "Waiting for custom fields to be visible"
    );

    const customModelName = doc.getElementById("customModelName");
    const customModelEndpoint = doc.getElementById("customModelEndpoint");
    const customModelAuthToken = doc.getElementById("customModelAuthToken");

    customModelEndpoint.value = "https://example.com";
    customModelEndpoint.dispatchEvent(new Event("change", { bubbles: true }));

    // Wait for button to be enabled
    const customModelSaveButton = doc.getElementById("customModelSaveButton");
    await BrowserTestUtils.waitForCondition(
      () => !customModelSaveButton.disabled,
      "Waiting for save button to be enabled"
    );

    customModelName.value = "my-custom-model";
    customModelName.dispatchEvent(new Event("input", { bubbles: true }));
    customModelAuthToken.value = "my-token";
    customModelAuthToken.dispatchEvent(new Event("input", { bubbles: true }));

    customModelSaveButton.scrollIntoView({});
    EventUtils.synthesizeMouseAtCenter(customModelSaveButton, {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        Services.prefs.getStringPref("browser.smartwindow.model", "") ===
        "my-custom-model",
      "Waiting for model to be saved via mouse"
    );

    Assert.equal(
      Services.prefs.getStringPref("browser.smartwindow.model"),
      "my-custom-model",
      "Model pref is saved via mouse"
    );

    // Reset for keyboard test
    Services.prefs.clearUserPref("browser.smartwindow.model");
    Services.prefs.clearUserPref("browser.smartwindow.endpoint");
    Services.prefs.clearUserPref("browser.smartwindow.apiKey");

    customModelName.value = "keyboard-model";
    customModelEndpoint.value = "https://example.com";
    customModelEndpoint.dispatchEvent(new Event("change", { bubbles: true }));
    customModelAuthToken.value = "keyboard-token";

    // Keyboard test to focus and space bar
    customModelSaveButton.focus();
    EventUtils.synthesizeKey(" ", {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        Services.prefs.getStringPref("browser.smartwindow.model", "") ===
        "keyboard-model",
      "Waiting for model to be saved via keyboard"
    );

    Assert.equal(
      Services.prefs.getStringPref("browser.smartwindow.model"),
      "keyboard-model",
      "Model pref is saved via keyboard"
    );
    Assert.equal(
      Services.prefs.getStringPref("browser.smartwindow.endpoint"),
      "https://example.com",
      "Endpoint pref is saved via keyboard"
    );
  });

  it("restores custom endpoint when switching back to custom", async () => {
    await SpecialPowers.pushPrefEnv({
      set: [
        ["browser.smartwindow.preferences.endpoint", "https://example.com"],
      ],
    });

    ({ doc, win } = await openSmartWindowPanel());

    const customRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-custom"]'
    );
    EventUtils.synthesizeMouseAtCenter(customRadio, {}, win);

    await BrowserTestUtils.waitForCondition(
      () =>
        BrowserTestUtils.isVisible(doc.getElementById("customModelEndpoint")),
      "Waiting for custom fields to be visible"
    );

    const customModelEndpoint = doc.getElementById("customModelEndpoint");

    await BrowserTestUtils.waitForCondition(
      () => customModelEndpoint.value === "https://example.com",
      "Waiting for endpoint to be restored in input"
    );

    Assert.equal(
      customModelEndpoint.value,
      "https://example.com",
      "Custom endpoint is restored in input"
    );
  });

  it("hides custom fields when preset model is selected", async () => {
    await SpecialPowers.pushPrefEnv({
      set: [["browser.smartwindow.firstrun.modelChoice", "0"]],
    });

    ({ doc, win } = await openSmartWindowPanel());

    const fastRadio = doc.querySelector(
      'moz-radio[data-l10n-id="smart-window-model-fast"]'
    );
    fastRadio.click();

    await BrowserTestUtils.waitForCondition(
      () => !BrowserTestUtils.isVisible(doc.getElementById("customModelName")),
      "Waiting for custom fields to be hidden"
    );

    const customModelName = doc.getElementById("customModelName");
    Assert.ok(
      !BrowserTestUtils.isVisible(customModelName),
      "Custom fields are hidden when preset is selected"
    );
  });

  it("shows custom as selected when user has custom endpoint", async () => {
    await SpecialPowers.pushPrefEnv({
      set: [
        ["browser.smartwindow.endpoint", "https://example.com"],
        ["browser.smartwindow.firstrun.modelChoice", "0"],
      ],
    });

    ({ doc, win } = await openSmartWindowPanel());

    const modelSelection = doc.getElementById("modelSelection");
    Assert.equal(
      modelSelection.value,
      "0",
      "Custom radio is selected when user has custom endpoint"
    );
  });

  it("populates custom fields with saved values", async () => {
    await SpecialPowers.pushPrefEnv({
      set: [
        ["browser.smartwindow.model", "saved-model"],
        ["browser.smartwindow.endpoint", "https://example.com"],
        ["browser.smartwindow.apiKey", "saved-token"],
        ["browser.smartwindow.firstrun.modelChoice", "0"],
      ],
    });

    ({ doc, win } = await openSmartWindowPanel());

    const customModelName = doc.getElementById("customModelName");
    const customModelEndpoint = doc.getElementById("customModelEndpoint");
    const customModelAuthToken = doc.getElementById("customModelAuthToken");

    Assert.equal(
      customModelName.value,
      "saved-model",
      "Model name will be populated"
    );
    Assert.equal(
      customModelEndpoint.value,
      "https://example.com",
      "Endpoint is populated"
    );
    Assert.equal(
      customModelAuthToken.value,
      "saved-token",
      "Auth token is populated"
    );
  });
});
