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

"use strict";

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

const SOURCE_PROFILE_DIR = "Library/Application Support/Google/Chrome/Default/";

const PROFILE = {
  id: "Default",
  name: "Person 1",
};

/**
 * TEST_URLS reflects the data stored in '${SOURCE_PROFILE_DIR}HistoryMaster'.
 * The main object reflects the data in the 'urls' table. The visits property
 * reflects the associated data in the 'visits' table.
 */
const TEST_URLS = [
  {
    id: 1,
    url: "http://example.com/",
    title: "test",
    visit_count: 1,
    typed_count: 0,
    last_visit_time: 13193151310368000,
    hidden: 0,
    visits: [
      {
        id: 1,
        url: 1,
        visit_time: 13193151310368000,
        from_visit: 0,
        transition: 805306370,
        segment_id: 0,
        visit_duration: 10745006,
        incremented_omnibox_typed_score: 0,
      },
    ],
  },
  {
    id: 2,
    url: "http://invalid.com/",
    title: "test2",
    visit_count: 1,
    typed_count: 0,
    last_visit_time: 13193154948901000,
    hidden: 0,
    visits: [
      {
        id: 2,
        url: 2,
        visit_time: 13193154948901000,
        from_visit: 0,
        transition: 805306376,
        segment_id: 0,
        visit_duration: 6568270,
        incremented_omnibox_typed_score: 0,
      },
    ],
  },
];

async function setVisitTimes(time) {
  let loginDataFile = do_get_file(`${SOURCE_PROFILE_DIR}History`);
  let dbConn = await Sqlite.openConnection({ path: loginDataFile.path });

  await dbConn.execute(`UPDATE urls SET last_visit_time = :last_visit_time`, {
    last_visit_time: time,
  });
  await dbConn.execute(`UPDATE visits SET visit_time = :visit_time`, {
    visit_time: time,
  });

  await dbConn.close();
}

function setExpectedVisitTimes(time) {
  for (let urlInfo of TEST_URLS) {
    urlInfo.last_visit_time = time;
    urlInfo.visits[0].visit_time = time;
  }
}

function assertEntryMatches(entry, urlInfo, dateWasInFuture = false) {
  info(`Checking url: ${urlInfo.url}`);
  Assert.ok(entry, `Should have stored an entry`);

  Assert.equal(entry.url, urlInfo.url, "Should have the correct URL");
  Assert.equal(entry.title, urlInfo.title, "Should have the correct title");
  Assert.equal(
    entry.visits.length,
    urlInfo.visits.length,
    "Should have the correct number of visits"
  );

  for (let index in urlInfo.visits) {
    Assert.equal(
      entry.visits[index].transition,
      PlacesUtils.history.TRANSITIONS.LINK,
      "Should have Link type transition"
    );

    if (dateWasInFuture) {
      Assert.lessOrEqual(
        entry.visits[index].date.getTime(),
        new Date().getTime(),
        "Should have moved the date to no later than the current date."
      );
    } else {
      Assert.equal(
        entry.visits[index].date.getTime(),
        ChromeMigrationUtils.chromeTimeToDate(
          urlInfo.visits[index].visit_time,
          new Date()
        ).getTime(),
        "Should have the correct date"
      );
    }
  }
}

function setupHistoryFile() {
  removeHistoryFile();
  let file = do_get_file(`${SOURCE_PROFILE_DIR}HistoryMaster`);
  file.copyTo(file.parent, "History");
}

function removeHistoryFile() {
  let file = do_get_file(`${SOURCE_PROFILE_DIR}History`, true);
  try {
    file.remove(false);
  } catch (ex) {
    // It is ok if this doesn't exist.
    if (ex.result != Cr.NS_ERROR_FILE_NOT_FOUND) {
      throw ex;
    }
  }
}

add_task(async function setup() {
  registerFakePath("ULibDir", do_get_file("Library/"));

  registerCleanupFunction(async () => {
    await PlacesUtils.history.clear();
    removeHistoryFile();
  });
});

add_task(async function test_import() {
  setupHistoryFile();
  await PlacesUtils.history.clear();
  // Update to ~10 days ago since the date can't be too old or Places may expire it.
  const pastDate = new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 10);
  const pastChromeTime = ChromeMigrationUtils.dateToChromeTime(pastDate);
  await setVisitTimes(pastChromeTime);
  setExpectedVisitTimes(pastChromeTime);

  let migrator = await MigrationUtils.getMigrator("chrome");
  Assert.ok(
    await migrator.isSourceAvailable(),
    "Sanity check the source exists"
  );

  await promiseMigration(
    migrator,
    MigrationUtils.resourceTypes.HISTORY,
    PROFILE
  );

  for (let urlInfo of TEST_URLS) {
    let entry = await PlacesUtils.history.fetch(urlInfo.url, {
      includeVisits: true,
    });
    assertEntryMatches(entry, urlInfo);
  }
});

add_task(async function test_import_future_date() {
  setupHistoryFile();
  await PlacesUtils.history.clear();
  const futureDate = new Date().getTime() + 6000 * 60 * 24;
  await setVisitTimes(ChromeMigrationUtils.dateToChromeTime(futureDate));

  let migrator = await MigrationUtils.getMigrator("chrome");
  Assert.ok(
    await migrator.isSourceAvailable(),
    "Sanity check the source exists"
  );

  await promiseMigration(
    migrator,
    MigrationUtils.resourceTypes.HISTORY,
    PROFILE
  );

  for (let urlInfo of TEST_URLS) {
    let entry = await PlacesUtils.history.fetch(urlInfo.url, {
      includeVisits: true,
    });
    assertEntryMatches(entry, urlInfo, true);
  }
});
