const { LoginManagerPrompter } = ChromeUtils.importESModule(
  "resource://gre/modules/LoginManagerPrompter.sys.mjs"
);

const TEST_CASES = [
  {
    description: "page values should appear before saved values",
    savedLogins: [{ username: "savedUsername", password: "savedPassword" }],
    possibleUsernames: ["pageUsername"],
    expectedSuggestions: [
      { text: "pageUsername", style: "possible-username" },
      { text: "savedUsername", style: "login" },
    ],
    isLoggedIn: true,
  },
  {
    description: "duplicate page values should be deduped",
    savedLogins: [],
    possibleUsernames: ["pageUsername", "pageUsername", "pageUsername2"],
    expectedSuggestions: [
      { text: "pageUsername", style: "possible-username" },
      { text: "pageUsername2", style: "possible-username" },
    ],
    isLoggedIn: true,
  },
  {
    description: "page values should dedupe and win over saved values",
    savedLogins: [{ username: "username", password: "savedPassword" }],
    possibleUsernames: ["username"],
    expectedSuggestions: [{ text: "username", style: "possible-username" }],
    isLoggedIn: true,
  },
  {
    description: "empty usernames should be filtered out",
    savedLogins: [{ username: "", password: "savedPassword" }],
    possibleUsernames: [""],
    expectedSuggestions: [],
    isLoggedIn: true,
  },
  {
    description: "auth logins should be displayed alongside normal ones",
    savedLogins: [
      { username: "normalUsername", password: "normalPassword" },
      { isAuth: true, username: "authUsername", password: "authPassword" },
    ],
    possibleUsernames: [""],
    expectedSuggestions: [
      { text: "normalUsername", style: "login" },
      { text: "authUsername", style: "login" },
    ],
    isLoggedIn: true,
  },
  {
    description: "saved logins from subdomains should be displayed",
    savedLogins: [
      {
        username: "savedUsername",
        password: "savedPassword",
        origin: "https://subdomain.example.com",
      },
    ],
    possibleUsernames: [],
    expectedSuggestions: [{ text: "savedUsername", style: "login" }],
    isLoggedIn: true,
  },
  {
    description: "usernames from different subdomains should be deduped",
    savedLogins: [
      {
        username: "savedUsername",
        password: "savedPassword",
        origin: "https://subdomain.example.com",
      },
      {
        username: "savedUsername",
        password: "savedPassword",
        origin: "https://example.com",
      },
    ],
    possibleUsernames: [],
    expectedSuggestions: [{ text: "savedUsername", style: "login" }],
    isLoggedIn: true,
  },
  {
    description: "No results with saved login when Primary Password is locked",
    savedLogins: [
      {
        username: "savedUsername",
        password: "savedPassword",
        origin: "https://example.com",
      },
    ],
    possibleUsernames: [],
    expectedSuggestions: [],
    isLoggedIn: false,
  },
];

const LOGIN = TestData.formLogin({
  origin: "https://example.com",
  formActionOrigin: "https://example.com",
  username: "LOGIN is used only for its origin",
  password: "LOGIN is used only for its origin",
});

function _setPrefs() {
  Services.prefs.setBoolPref("signon.capture.inputChanges.enabled", true);
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("signon.capture.inputChanges.enabled");
  });
}

async function _saveLogins(loginDatas) {
  const logins = loginDatas.map(loginData => {
    let login;
    if (loginData.isAuth) {
      login = TestData.authLogin({
        origin: loginData.origin ?? "https://example.com",
        httpRealm: "example-realm",
        username: loginData.username,
        password: loginData.password,
      });
    } else {
      login = TestData.formLogin({
        origin: loginData.origin ?? "https://example.com",
        formActionOrigin: "https://example.com",
        username: loginData.username,
        password: loginData.password,
      });
    }
    return login;
  });
  await Services.logins.addLogins(logins);
}

function _compare(expectedArr, actualArr) {
  Assert.ok(!!expectedArr, "Expect expectedArr to be truthy");
  Assert.ok(!!actualArr, "Expect actualArr to be truthy");
  Assert.equal(
    expectedArr.length,
    actualArr.length,
    "Expect expectedArr and actualArr to be the same length"
  );
  for (let i = 0; i < expectedArr.length; i++) {
    const expected = expectedArr[i];
    const actual = actualArr[i];

    Assert.equal(
      expected.text,
      actual.text,
      `Expect element #${i} text to match.  Expected: '${expected.text}', Actual '${actual.text}'`
    );
    Assert.equal(
      expected.style,
      actual.style,
      `Expect element #${i} text to match.  Expected: '${expected.style}', Actual '${actual.style}'`
    );
  }
}

async function _test(testCase) {
  info(`Starting test case: ${testCase.description}`);
  info(`Storing saved logins: ${JSON.stringify(testCase.savedLogins)}`);
  await _saveLogins(testCase.savedLogins);

  if (!testCase.isLoggedIn) {
    // Primary Password should be enabled and locked
    LoginTestUtils.primaryPassword.enable();
  }

  info("Computing results");
  const result = await LoginManagerPrompter._getUsernameSuggestions(
    LOGIN,
    testCase.possibleUsernames
  );

  _compare(testCase.expectedSuggestions, result);

  info("Cleaning up state");
  if (!testCase.isLoggedIn) {
    LoginTestUtils.primaryPassword.disable();
  }
  LoginTestUtils.clearData();
}

add_task(async function test_LoginManagerPrompter_getUsernameSuggestions() {
  _setPrefs();
  for (const tc of TEST_CASES) {
    await _test(tc);
  }
});
