'use strict';

// ----------------- Internationalization ------------------
document.querySelectorAll('[data-i18n]').forEach(node => {
  let [text, attr] = node.dataset.i18n.split('|');
  text = chrome.i18n.getMessage(text);
  attr ? node[attr] = text : node.appendChild(document.createTextNode(text));
});
// ----------------- /Internationalization -----------------

document.addEventListener('keyup', evt => {
  if (evt.keyCode === 27) {
    location.href = '/options.html';
  }
});

// ----------------- Spinner -------------------------------
const spinner = document.querySelector('.spinner');
function hideSpinner() {

  spinner.classList.remove('on');
  setTimeout(() => { spinner.style.display = 'none'; }, 600);
}

function showSpinner() {

  spinner.style.display = 'flex';
  spinner.classList.add('on');
}
// ----------------- /spinner ------------------------------
document.addEventListener('DOMContentLoaded', () => {
  hideSpinner();
});

// addEventListener for all buttons & handle together
document.querySelectorAll('button').forEach(item => item.addEventListener('click', process));

let proxiesAdded = 0; // Global to this module in case user does multiple bulk imports before closing import-bulk.html

function process(e) {
  switch (this.id || this.dataset.i18n) {
    case 'back': location.href = '/options.html'; break;
    case 'import': imp0rt(); break;
  }
}

function imp0rt() {
  const {parsedList, skippedList} = parseList(document.getElementById('proxyList').value);
  if (parsedList.length > 0) {
    if (document.querySelector('#overwrite').checked) {
      if (confirm(chrome.i18n.getMessage('confirmOverwrite'))) {
        showSpinner();
        chrome.storage.local.clear(() => chrome.storage.sync.clear(() => {
          hideSpinner();
          storeProxies(parsedList);
        }));
      }
      else {
        return;
      }
    }
    else {
      storeProxies(parsedList);
    }
  }
  if (skippedList.length > 0) {
    alert(`${chrome.i18n.getMessage('importsSkipped', [skippedList.length + "", skippedList.toString()])}`);
  }
  if (parsedList.length > 0) {
    alert(`${chrome.i18n.getMessage('importSucceeded', [parsedList.length])}`);
  }
  location.href = '/options.html';
}

function parseList(rawList) {
  const parsedList = [], skippedList = [], colors = ['#663300', '#284B63', '#C99656', '#7B758C', '#171E1D'];
  if (!rawList) {
    return {parsedList, skippedList};
  }
  rawList.split('\n').forEach((item) => {
    if (!item) {
      return; // continue to next
    }
    let p, patternIncludesAll = true, patternExcludesIntranet = true;
    // Is this line simple or complete format?
    let protocol = item.match(/.+:\/\//); // null for strings like 127.0.0.1:3128 (simple format)
    if (protocol) {
      // This line is uses 'complete' format
      let url;
      try {
        // In Firefox 78.0.2, the built-in javascript URL class will not parse URLs with custom schemes/protocols
        // like socks://127.0.0.1. However, Chrome 84.0.4147.89 and Node 14.5.0 both do. In order to be compatible
        // with Firefox, let's replace the scheme/protocol with 'http'. We could also instead write our own parsing
        // logic with a regular expression, but that does not seems necessary.
        if (protocol[0] !== 'http://' && protocol[0] !== 'https://') {
          item = 'http://' + item.substring(protocol[0].length);
          url = new URL(item);
          protocol = protocol[0].substring(0, protocol[0].length-2); //strip ending //
        }
        else {
          url = new URL(item);
          protocol = url.protocol;
        }
      }
      catch (e) {
        console.log(e);
        // URL couldn't be parsed
        skippedList.push(item);
        return; // continue to next
      }
      const type = protocol === 'proxy:' || protocol === 'http:' ? PROXY_TYPE_HTTP :
        protocol === 'ssl:' || protocol === 'https:' ? PROXY_TYPE_HTTPS :
        protocol === 'socks:' || protocol === 'socks5:' ? PROXY_TYPE_SOCKS5 :
        protocol === 'socks4:' ? PROXY_TYPE_SOCKS4 : -1;
        if (type === -1) {
          console.log("unknown protocol");
          skippedList.push(item);
          return; // continue to next
        }

        // If color not specified in the URL, then rotate among the ones in the colors array.
        const color = url.searchParams.get('color') ?
          ('#' + url.searchParams.get('color')) : colors[parsedList.length % colors.length];

        const title = url.searchParams.get('title');
        const countryCode = url.searchParams.get('countryCode') || url.searchParams.get('cc');
        const country = url.searchParams.get('country') || countryCode;

        // If paramName url param is not specified or it's specified and not 'false', then paramValue should equal true.
        // We assume true in case the param is absent, which may be counterintuitive, but this fcn is used for params that
        // we want to assume true when absent.
        function parseBooleanParam(url, paramName, aliasParamName) {
          const paramValue = url.searchParams.get(paramName) || (aliasParamName && url.searchParams.get(aliasParamName));
          return paramValue ? !(paramValue.toLowerCase() === 'false') : true;
        }
        const proxyDNS = parseBooleanParam(url, 'proxyDns');
        const active = parseBooleanParam(url, 'enabled', 'active');

        patternIncludesAll = parseBooleanParam(url, 'patternIncludesAll');
        patternExcludesIntranet = parseBooleanParam(url, 'patternExcludesIntranet');

        // the URL class sets port === '' if not specified on the URL or it's an invalid port e.g. contains alpha chars
        let port = url.port;
        if (port === '') {
          // Default ports are 3128 for HTTP proxy, 443 for tls/ssl/https proxy, 1080 for socks4/5
          port = type === PROXY_TYPE_HTTP ? 3128 : type === PROXY_TYPE_HTTPS ? 443 : 1080;
        }

        console.log(url);
        // the URL class sets username and password === '' if not specified on the URL
        p = {type, username: url.username, password: url.password, address: url.hostname, port, color, title, proxyDNS, active, countryCode, country};
    }
    else {
      // simple
      const splitItem = item.split(':');
      // Split always returns an array no matter what
      p = {address: splitItem[0], port: splitItem[1], username: splitItem[2], password: splitItem[3], color: colors[parsedList.length % colors.length]};
    }

    const proxy = makeProxy(p, patternIncludesAll, patternExcludesIntranet);
    if (proxy) {
      parsedList.push(proxy);
    }
    else {
      skippedList.push(item);
    }

  }); //forEach

  return {parsedList, skippedList};
}

function makeProxy({type = PROXY_TYPE_HTTP, username, password, address, port, color, title, proxyDNS, active = true, countryCode, country},
  patternIncludesAll, patternExcludesIntranet) {

  port = port*1; // convert to digit
  if (!port || port < 1) { // is port NaN or less than 1
    console.log("port is NaN or less than 1");
    return null;
  }

  // strip bad chars from all input except username, password, type, proxyDNS, and active
  // (those last 3 are forced to boolean types before we are called)
  // If we do strip bad chars from usernams or password, auth could fail.
  address = Utils.stripBadChars(address);
  color = Utils.stripBadChars(color);
  title = Utils.stripBadChars(title);
  countryCode = Utils.stripBadChars(countryCode);
  country = Utils.stripBadChars(country);

  if (!address) {
    console.log("no address");
    return null;
  }

  const proxy = {type, address, port, color, active};

  // Only set the properties needed. null and undefined props seem to be saved if set, so don't set them.
  function setPropertyIfHasValue(prop, value, proxy) {
    if (value || value === 0) {
      proxy[prop] = value;
    }
  }
  setPropertyIfHasValue('username', username, proxy);
  setPropertyIfHasValue('password', password, proxy);
  setPropertyIfHasValue('title', title, proxy);
  setPropertyIfHasValue('cc', countryCode, proxy);
  setPropertyIfHasValue('country', country, proxy);

  if (type === PROXY_TYPE_SOCKS5) {
    // Only set if socks5
    proxy.proxyDNS = proxyDNS;
  }

  if (FOXYPROXY_BASIC) {
    proxy.whitePatterns = proxy.blackPatterns = [];
  }
  else {
    proxy.whitePatterns = patternIncludesAll ? [PATTERN_ALL_WHITE] : [];
    proxy.blackPatterns = patternExcludesIntranet ? [...blacklistSet] : [];
  }
  return proxy;
}

function storeProxies(parsedList) {
  const sync = localStorage.getItem('sync') === 'true';
  const storageArea = !sync ? chrome.storage.local : chrome.storage.sync;

  for (const idx in parsedList) {
    const proxy = parsedList[idx];
    console.log(proxy);
    // Get the nextIndex given to us by options.js and add by the number of proxies we've added.
    // This ensures this proxy setting is last in list of all proxy settings.

    proxy.index = (localStorage.getItem('nextIndex')) + (++proxiesAdded);
    storageArea.set({[Utils.getUniqueId()]: proxy}, () => {
      console.log(`stored proxy`);
    });
  }
}
