#!/usr/bin/env node

const axios = require('axios');
const fs = require('fs-extra');
const os = require('os');
const path = require('path');
const { pipeline } = require('stream/promises');
const unzipper = require('unzipper');

const binaries = {
  darwin: {
    x64: 'go2rtc_mac_amd64.zip',
    arm64: 'go2rtc_mac_arm64.zip',
  },
  linux: {
    x64: 'go2rtc_linux_amd64',
    arm: 'go2rtc_linux_arm',
    armv6: 'go2rtc_linux_armv6',
    arm64: 'go2rtc_linux_arm64',
    i386: 'go2rtc_linux_i386',
    mipsel: 'go2rtc_linux_mipsel',
  },
  win32: {
    x64: 'go2rtc_win64.zip',
    arm64: 'go2rtc_win_arm64.zip',
    i386: 'go2rtc_win32.zip',
  },
};

const GO2RTC_VERSION = 'v1.8.5';
const arch = os.arch();
const platform = os.platform();
const filename = binaries[platform]?.[arch];
const releasesUrl = 'https://api.github.com/repos/AlexxIT/go2rtc/releases';

console.log(`Detected platform: ${platform} / ${arch}`);

const go2rtcBinaryPath = path.resolve(__dirname, '../binary');
const go2rtcFilePath = path.resolve(go2rtcBinaryPath, filename || '');
const go2rtcExtractedFileName = 'go2rtc' + (platform === 'win32' ? '.exe' : '');
const go2rtcExtractedFilePath = path.resolve(go2rtcBinaryPath, go2rtcExtractedFileName);

const isZipUrl = (url) => {
  const pathArray = new URL(url).pathname.split('/');
  const fileName = pathArray[pathArray.length - 1];

  return fileName !== undefined && path.extname(fileName) === '.zip';
};

const downloadFile = async (url) => {
  console.log(`Downloading Go2Rtc ${GO2RTC_VERSION} to ${go2rtcFilePath}...`);

  const { data } = await axios.get(url, {
    onDownloadProgress: (progressEvent) => {
      if (process.env.CI || !progressEvent.total) {
        return;
      }

      const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100) + '%';
      process.stdout.write('\r' + percent);

      if (progressEvent.loaded === progressEvent.total) {
        process.stdout.write('\r');
      }
    },
    timeout: 30 * 1000, // 30s
    maxRedirects: 3,
    responseType: 'stream',
  });

  await fs.ensureDir(go2rtcBinaryPath);

  const streams = [data, fs.createWriteStream(go2rtcFilePath)];

  await pipeline(streams);

  if (isZipUrl(url)) {
    console.log(`Extracting ${go2rtcFilePath} to ${go2rtcExtractedFilePath}...`);

    const directory = await unzipper.Open.file(go2rtcFilePath);

    await new Promise((resolve, reject) => {
      directory.files[0].stream().pipe(fs.createWriteStream(go2rtcExtractedFilePath)).on('error', reject).on('finish', resolve);
    });

    console.log(`Removing ${go2rtcFilePath}...`);

    await fs.remove(go2rtcFilePath);
  } else {
    console.log(`Renaming ${go2rtcFilePath} to ${go2rtcExtractedFilePath}...`);
    await fs.rename(go2rtcFilePath, go2rtcExtractedFilePath);
  }
};

const getReleaseAssets = async (version) => {
  const response = await axios.get(`${releasesUrl}/tags/${version}`);

  const assets = response.data.assets.map((asset) => asset.browser_download_url);
  const files = assets.map((asset) => asset.split(`${version}/`)[1]);

  return {
    assets,
    files,
  };
};

const downloadGo2Rtc = async () => {
  const release = await getReleaseAssets(GO2RTC_VERSION);

  if (!filename || !release.assets.find((r) => r.endsWith(filename))) {
    throw new Error(`No go2rtc binary found for architecture (${platform} / ${arch})`);
  }

  const downloadUrl = release.assets.find((r) => r.endsWith(filename));

  await downloadFile(downloadUrl);

  if (platform === 'linux' || platform === 'darwin') {
    console.log(`Making ${go2rtcExtractedFilePath} executable...`);
    fs.chmodSync(go2rtcExtractedFilePath, 0o755);
  }

  console.log('Done!');
};

downloadGo2Rtc();
