/* global Android */
let html;
try {
  html = require('choo/html');
} catch (e) {
  // running in the service worker
}
const b64 = require('base64-js');

function arrayToB64(array) {
  return b64
    .fromByteArray(array)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
}

function b64ToArray(str) {
  return b64.toByteArray(str + '==='.slice((str.length + 3) % 4));
}

function locale() {
  return document.querySelector('html').lang;
}

function isFile(id) {
  return /^[0-9a-fA-F]{10,16}$/.test(id);
}

async function copyToClipboard(str) {
  try {
    await navigator.clipboard.writeText(str);
  } catch {
    // Older browsers or the clipboard API fails because of a missing permission
    const aux = document.createElement('input');
    aux.setAttribute('value', str);
    aux.contentEditable = true;
    aux.readOnly = true;
    document.body.appendChild(aux);
    if (navigator.userAgent.match(/iphone|ipad|ipod/i)) {
      const range = document.createRange();
      range.selectNodeContents(aux);
      const sel = getSelection();
      sel.removeAllRanges();
      sel.addRange(range);
      aux.setSelectionRange(0, str.length);
    } else {
      aux.select();
    }
    const result = document.execCommand('copy');
    document.body.removeChild(aux);
    return result;
  }
}

const LOCALIZE_NUMBERS = !!(
  typeof Intl === 'object' &&
  Intl &&
  typeof Intl.NumberFormat === 'function' &&
  typeof navigator === 'object'
);

const UNITS = ['bytes', 'kb', 'mb', 'gb'];
function bytes(num) {
  if (num < 1) {
    return '0B';
  }
  const exponent = Math.min(Math.floor(Math.log10(num) / 3), UNITS.length - 1);
  const n = Number(num / Math.pow(1024, exponent));
  const decimalDigits = Math.floor(n) === n ? 0 : 1;
  let nStr = n.toFixed(decimalDigits);
  if (LOCALIZE_NUMBERS) {
    try {
      nStr = n.toLocaleString(locale(), {
        minimumFractionDigits: decimalDigits,
        maximumFractionDigits: decimalDigits
      });
    } catch (e) {
      // fall through
    }
  }
  return translate('fileSize', {
    num: nStr,
    units: translate(UNITS[exponent])
  });
}

function percent(ratio) {
  if (LOCALIZE_NUMBERS) {
    try {
      return ratio.toLocaleString(locale(), { style: 'percent' });
    } catch (e) {
      // fall through
    }
  }
  return `${Math.floor(ratio * 100)}%`;
}

function number(n) {
  if (LOCALIZE_NUMBERS) {
    return n.toLocaleString(locale());
  }
  return n.toString();
}

function allowedCopy() {
  const support = !!document.queryCommandSupported;
  return support ? document.queryCommandSupported('copy') : false;
}

function delay(delay = 100) {
  return new Promise(resolve => setTimeout(resolve, delay));
}

function fadeOut(selector) {
  const classes = document.querySelector(selector).classList;
  classes.remove('effect--fadeIn');
  classes.add('effect--fadeOut');
  return delay(300);
}

function openLinksInNewTab(links, should = true) {
  links = links || Array.from(document.querySelectorAll('a:not([target])'));
  if (should) {
    links.forEach(l => {
      l.setAttribute('target', '_blank');
      l.setAttribute('rel', 'noopener noreferrer');
    });
  } else {
    links.forEach(l => {
      l.removeAttribute('target');
      l.removeAttribute('rel');
    });
  }
  return links;
}

function browserName() {
  try {
    // order of these matters
    if (/firefox/i.test(navigator.userAgent)) {
      return 'firefox';
    }
    if (/edge/i.test(navigator.userAgent)) {
      return 'edge';
    }
    if (/edg/i.test(navigator.userAgent)) {
      return 'edgium';
    }
    if (/trident/i.test(navigator.userAgent)) {
      return 'ie';
    }
    if (/chrome/i.test(navigator.userAgent)) {
      return 'chrome';
    }
    if (/safari/i.test(navigator.userAgent)) {
      return 'safari';
    }
    if (/send android/i.test(navigator.userAgent)) {
      return 'android-app';
    }
    return 'other';
  } catch (e) {
    return 'unknown';
  }
}

async function streamToArrayBuffer(stream, size) {
  const reader = stream.getReader();
  let state = await reader.read();

  if (size) {
    const result = new Uint8Array(size);
    let offset = 0;
    while (!state.done) {
      result.set(state.value, offset);
      offset += state.value.length;
      state = await reader.read();
    }
    return result.buffer;
  }

  const parts = [];
  let len = 0;
  while (!state.done) {
    parts.push(state.value);
    len += state.value.length;
    state = await reader.read();
  }
  let offset = 0;
  const result = new Uint8Array(len);
  for (const part of parts) {
    result.set(part, offset);
    offset += part.length;
  }
  return result.buffer;
}

function list(items, ulStyle = '', liStyle = '') {
  const lis = items.map(
    i =>
      html`
        <li class="${liStyle}">${i}</li>
      `
  );
  return html`
    <ul class="${ulStyle}">
      ${lis}
    </ul>
  `;
}

function secondsToL10nId(seconds) {
  if (seconds < 3600) {
    return { id: 'timespanMinutes', num: Math.floor(seconds / 60) };
  } else if (seconds < 86400) {
    return { id: 'timespanHours', num: Math.floor(seconds / 3600) };
  } else {
    return { id: 'timespanDays', num: Math.floor(seconds / 86400) };
  }
}

function timeLeft(milliseconds) {
  if (milliseconds < 1) {
    return { id: 'linkExpiredAlt' };
  }
  const minutes = Math.floor(milliseconds / 1000 / 60);
  const hours = Math.floor(minutes / 60);
  const days = Math.floor(hours / 24);
  if (days >= 1) {
    return {
      id: 'expiresDaysHoursMinutes',
      days,
      hours: hours % 24,
      minutes: minutes % 60
    };
  }
  if (hours >= 1) {
    return {
      id: 'expiresHoursMinutes',
      hours,
      minutes: minutes % 60
    };
  } else if (hours === 0) {
    if (minutes === 0) {
      return { id: 'expiresMinutes', minutes: '< 1' };
    }
    return { id: 'expiresMinutes', minutes };
  }
  return null;
}

function platform() {
  if (typeof Android === 'object') {
    return 'android';
  }
  return 'web';
}

const ECE_RECORD_SIZE = 1024 * 64;
const TAG_LENGTH = 16;
function encryptedSize(size, rs = ECE_RECORD_SIZE, tagLength = TAG_LENGTH) {
  const chunk_meta = tagLength + 1; // Chunk metadata, tag and delimiter
  return 21 + size + chunk_meta * Math.ceil(size / (rs - chunk_meta));
}

let translate = function() {
  throw new Error('uninitialized translate function. call setTranslate first');
};
function setTranslate(t) {
  translate = t;
}

module.exports = {
  locale,
  fadeOut,
  delay,
  allowedCopy,
  bytes,
  percent,
  number,
  copyToClipboard,
  arrayToB64,
  b64ToArray,
  isFile,
  openLinksInNewTab,
  browserName,
  streamToArrayBuffer,
  list,
  secondsToL10nId,
  timeLeft,
  platform,
  encryptedSize,
  setTranslate
};