'use strict';

var uaDetect = module.exports;

function getRequest(req) {
  var ua = req.headers['user-agent'] || '';
  var os = req.query.os;
  var arch = req.query.arch;
  var scheme = req.socket.encrypted ? 'https' : 'http';
  var host = req.headers.host || 'beta.webinstall.dev';
  var url = scheme + '://' + host + '/api/debug';
  if (os && arch) {
    ua = os + ' ' + arch;
  } else if (os || arch) {
    ua = os || arch;
  }

  return {
    unix: 'curl -fsSA "$(uname -srm)" ' + url,
    windows: 'curl.exe -fsSA "MS $Env:PROCESSOR_ARCHITECTURE" ' + url,
    ua: ua,
    os: uaDetect.os(ua),
    arch: uaDetect.arch(ua),
  };
}

function getOs(ua) {
  if ('-' === ua) {
    return '-';
  }

  if (/Android/i.test(ua)) {
    // android must be tested before linux
    return 'android';
  } else if (/iOS|iPhone|Macintosh|Darwin|OS\s*X|macOS|mac/i.test(ua)) {
    return 'macos';
  } else if (/Linux/i.test(ua) && !/cygwin|msysgit/i.test(ua)) {
    // It's the year of the Linux Desktop!
    // See also http://www.mslinux.org/
    // 'linux' must be tested before 'Microsoft' because WSL
    return 'linux';
  } else if (/^ms$|Microsoft|Windows|win32|win|PowerShell|CYGWIN|MINGW/i.test(ua)) {
    // 'win' must be tested after 'darwin'
    return 'windows';
  } else if (/Linux|curl|wget/i.test(ua)) {
    // test 'linux' again, after 'win'
    return 'linux';
  } else {
    return 'error';
  }
}

function getArch(ua) {
  if ('-' === ua) {
    return '-';
  }

  // Quick hack for Apple Silicon M1
  //
  // Note: we now use `uname -srm` which does not have the native arch
  // info included with `uname -v` and `uname -a`.
  //
  // Native:  Darwin boomer.local 20.2.0 Darwin Kernel Version 20.2.0: Wed Dec  2 20:40:21 PST 2020; root:xnu-7195.60.75~1/RELEASE_ARM64_T8101 arm64
  // Resetta: Darwin boomer.local 20.2.0 Darwin Kernel Version 20.2.0: Wed Dec  2 20:40:21 PST 2020; root:xnu-7195.60.75~1/RELEASE_ARM64_T8101 x86_64
  ua = ua.replace(/xnu-.*RELEASE_[^\s]*/, '');

  if (/aarch64|arm64|arm8|armv8/i.test(ua)) {
    return 'arm64';
  } else if (/aarch|arm7|armv7|arm32/i.test(ua)) {
    return 'armv7l';
  } else if (/arm6|armv6|arm(\b|_)/i.test(ua)) {
    return 'armv6l';
  } else if (/ppc64le/i.test(ua)) {
    return 'ppc64le';
  } else if (/ppc64/i.test(ua)) {
    return 'ppc64';
  } else if (/mips64/i.test(ua)) {
    return 'mips64';
  } else if (/mips/i.test(ua)) {
    return 'mips';
  } else if (/(amd64|x64|_64)\b/i.test(ua)) {
    // must come after ppc64/mips64
    return 'amd64';
  } else if (/(3|6|x|_)86\b/i.test(ua)) {
    // must come after x86_64
    return 'x86';
  } else {
    // TODO handle explicit invalid different
    return 'error';
  }
}

function getLibc(ua) {
  if ('-' === ua) {
    return '-';
  }

  // How to see a bunch of target host quadruples:
  //     go tool dist list
  //     rustup target list
  //     zig targets | jq -r '.libc[]' | sort -

  // Generally, we prefer 'musl' builds because they DO work on glibc systems (Ubuntu),
  // but 'glibc' builds will NOT work on musl systems (Alpine / Docker).
  //
  // However, there are a few instances (ex: Node.js), where the 'musl' builds
  // DO NOT work on glibc systems.

  {
    let muslRe = /(\b|_)(musl)(\b|_)/i;
    if (muslRe.test(ua)) {
      return 'musl';
    }
  }

  {
    let msvcRe = /(\b|_)(msvc|windows|microsoft)(\b|_)/i;
    if (msvcRe.test(ua)) {
      return 'msvc';
    }
  }

  {
    let gnuRe = /(\b|_)(gnu|glibc|linux)(\b|_)/i;
    if (gnuRe.test(ua)) {
      return 'gnu';
    }
  }

  {
    let libcRe = /(\b|_)(libc)(\b|_)/i;
    if (libcRe.test(ua)) {
      return 'libc';
    }
  }

  {
    let darwinRe = /(\b|_)(darwin)(\b|_)/i;
    if (darwinRe.test(ua)) {
      // not sure whether this should be "darwin" or "libc" or "none"
      // https://opensource.apple.com/source/Libc/
      return 'libc';
    }
  }

  return 'libc';
}

uaDetect.os = getOs;
uaDetect.arch = getArch;
uaDetect.libc = getLibc;
uaDetect.request = getRequest;
