/*
# 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 http://mozilla.org/MPL/2.0/.
*/
'use strict';

import {
  log as internalLogger,
  configs,
  wait,
} from './common.js';
import * as ApiTabs from './api-tabs.js';
import * as Constants from './constants.js';
import * as TabsStore from './tabs-store.js';

// eslint-disable-next-line no-unused-vars
function log(...args) {
  internalLogger('common/unique-id', ...args);
}

//===================================================================
// Unique Tab ID
//===================================================================

// for generated IDs
const kID_ADJECTIVES = `
Agile
Breezy
Cheerful
Dapper
Edgy
Feisty
Gutsy
Hoary
Intrepid
Jaunty
Karmic
Lucid
Marveric
Natty
Oneiric
Precise
Quantal
Raring
Saucy
Trusty
Utopic
Vivid
Warty
Xenial
Yakkety
Zesty
`.toLowerCase().trim().split(/\s+/);

const kID_NOUNS = `
Alpaca
Badger
Cat
Drake
Eft
Fawn
Gibbon
Heron
Ibis
Jackalope
Koala
Lynx
Meerkat
Narwhal
Ocelot
Pangolin
Quetzal
Ringtail
Salamander
Tahr
Unicorn
Vervet
Werwolf
Xerus
Yak
Zapus
`.toLowerCase().trim().split(/\s+/);

let mReadyToDetectDuplicatedTab = false;

export function readyToDetectDuplicatedTab() {
  mReadyToDetectDuplicatedTab = true;
}

export async function request(tabOrId, options = {}) {
  if (typeof options != 'object')
    options = {};

  let tab = tabOrId;
  if (typeof tabOrId == 'number')
    tab = TabsStore.tabs.get(tabOrId);

  if (TabsStore.getCurrentWindowId()) {
    return browser.runtime.sendMessage({
      type:  Constants.kCOMMAND_REQUEST_UNIQUE_ID,
      tabId: tab.id
    }).catch(ApiTabs.createErrorHandler());
  }

  let originalId    = null;
  let originalTabId = null;
  let duplicated    = false;
  if (!options.forceNew) {
    // https://github.com/piroor/treestyletab/issues/2845
    // This delay may break initial restoration of tabs, so we should
    // ignore it until all restoration processes are finished.
    if (mReadyToDetectDuplicatedTab &&
        configs.delayForDuplicatedTabDetection > 0)
      await wait(configs.delayForDuplicatedTabDetection);

    let oldId = await browser.sessions.getTabValue(tab.id, Constants.kPERSISTENT_ID).catch(ApiTabs.createErrorHandler());
    if (oldId && !oldId.tabId) // ignore broken information!
      oldId = null;

    if (oldId) {
      // If the tab detected from stored tabId is different, it is duplicated tab.
      try {
        const tabWithOldId = TabsStore.tabs.get(oldId.tabId);
        if (!tabWithOldId)
          throw new Error(`Invalid tab ID: ${oldId.tabId}`);
        originalId = (tabWithOldId.$TST.uniqueId || await tabWithOldId.$TST.promisedUniqueId).id;
        duplicated = tab && tabWithOldId.id != tab.id && originalId == oldId.id;
        if (duplicated)
          originalTabId = oldId.tabId;
        else
          throw new Error(`Invalid tab ID: ${oldId.tabId}`);
      }
      catch(e) {
        ApiTabs.handleMissingTabError(e);
        // It fails if the tab doesn't exist.
        // There is no live tab for the tabId, thus
        // this seems to be a tab restored from session.
        // We need to update the related tab id.
        browser.sessions.setTabValue(tab.id, Constants.kPERSISTENT_ID, {
          id:    oldId.id,
          tabId: tab.id
        }).catch(ApiTabs.createErrorSuppressor());
        return {
          id:            oldId.id,
          originalId:    null,
          originalTabId: oldId.tabId,
          restored:      true
        };
      }
    }
  }

  const id = `tab-${generate()}`;
  // tabId is for detecttion of duplicated tabs
  await browser.sessions.setTabValue(tab.id, Constants.kPERSISTENT_ID, { id, tabId: tab.id }).catch(ApiTabs.createErrorSuppressor());
  return { id, originalId, originalTabId, duplicated };
}

function generate() {
  const adjective   = kID_ADJECTIVES[Math.floor(Math.random() * kID_ADJECTIVES.length)];
  const noun        = kID_NOUNS[Math.floor(Math.random() * kID_NOUNS.length)];
  const randomValue = Math.floor(Math.random() * 1000);
  return `${adjective}-${noun}-${Date.now()}-${randomValue}`;
}

export async function getFromTabs(tabs) {
  return Promise.all(tabs.map(tab =>
    browser.sessions.getTabValue(tab.id, Constants.kPERSISTENT_ID).catch(ApiTabs.createErrorHandler())
  ));
}

export async function ensureWindowId(windowId) {
  const storedUniqueId = await browser.sessions.getWindowValue(windowId, 'uniqueId').catch(_ => null);
  if (storedUniqueId)
    return storedUniqueId;

  const uniqueId = `window-${generate()}`;
  await browser.sessions.setWindowValue(windowId, 'uniqueId', uniqueId).catch(_ => null);
  return uniqueId;
}
