// download the ipfs binaries before building the electron clients

import fs from 'fs-extra';
import ProgressBar from 'progress';
import https from 'https';
import decompress from 'decompress';
import path from 'path';
import { fileURLToPath } from 'url';
const ipfsClientsPath = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'bin');
const ipfsClientWindowsPath = path.join(ipfsClientsPath, 'win');
const ipfsClientMacPath = path.join(ipfsClientsPath, 'mac');
const ipfsClientLinuxPath = path.join(ipfsClientsPath, 'linux');
const kuboReleaseBaseUrl = 'https://github.com/ipfs/kubo/releases/download';

const ipfsClientVersion = '0.39.0';

// Resolve desired build arch: allow overriding via env (so cross-arch builds pick correct binary)
const resolveBuildArch = () => {
  const envArch = process.env.BUILD_ARCH;
  if (envArch === 'arm64' || envArch === 'x64') return envArch;
  // fallback to host arch
  if (process.arch === 'arm64') return 'arm64';
  return 'x64';
};

const toKuboArch = (arch) => (arch === 'arm64' ? 'arm64' : 'amd64');

const getKuboUrl = (platform) => {
  const arch = toKuboArch(resolveBuildArch());
  if (platform === 'win32') return `${kuboReleaseBaseUrl}/v${ipfsClientVersion}/kubo_v${ipfsClientVersion}_windows-${arch}.zip`;
  if (platform === 'darwin') return `${kuboReleaseBaseUrl}/v${ipfsClientVersion}/kubo_v${ipfsClientVersion}_darwin-${arch}.tar.gz`;
  if (platform === 'linux') return `${kuboReleaseBaseUrl}/v${ipfsClientVersion}/kubo_v${ipfsClientVersion}_linux-${arch}.tar.gz`;
  // default to linux
  return `${kuboReleaseBaseUrl}/v${ipfsClientVersion}/kubo_v${ipfsClientVersion}_linux-${arch}.tar.gz`;
};

const downloadWithProgress = (url) =>
  new Promise((resolve, reject) => {
    const split = url.split('/');
    const fileName = split[split.length - 1];
    const chunks = [];
    const req = https.request(url);
    req.on('error', (err) => {
      console.error(`Error making request for ${url}:`, err);
      reject(err);
    });
    req.on('response', (res) => {
      res.on('error', (err) => {
        console.error(`Error in response for ${url}:`, err);
        reject(err);
      });
      // handle redirects
      if (res.statusCode == 301 || res.statusCode === 302) {
        resolve(downloadWithProgress(res.headers.location));
        return;
      }

      if (!res.statusCode || res.statusCode >= 400) {
        reject(new Error(`Download request for ${url} failed with status ${res.statusCode ?? 'unknown'}`));
        return;
      }

      const len = parseInt(res.headers['content-length'], 10);
      console.log();
      const bar = new ProgressBar(`  ${fileName} [:bar] :rate/bps :percent :etas`, {
        complete: '=',
        incomplete: ' ',
        width: 20,
        total: len,
        stream: process.stdout,
      });
      res.on('data', (chunk) => {
        chunks.push(chunk);
        bar.tick(chunk.length);
      });
      res.on('end', () => {
        console.log('\n');
        resolve(Buffer.concat(chunks));
      });
    });
    req.end();
  });

// add retry wrapper around downloadWithProgress to handle transient network errors
const downloadWithRetry = async (url, retries = 3) => {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      return await downloadWithProgress(url);
    } catch (err) {
      console.warn(`Download attempt ${attempt} for ${url} failed:`, err);
      if (attempt === retries) throw err;
      // wait before retrying
      await new Promise((res) => setTimeout(res, attempt * 1000));
    }
  }
};

// official kubo downloads need to be extracted
const downloadAndExtract = async (url, destinationPath) => {
  let binName = 'ipfs';
  if (destinationPath.endsWith('win')) {
    binName += '.exe';
  }
  const binPath = path.join(destinationPath, binName);
  if (fs.pathExistsSync(binPath)) {
    return;
  }
  console.log(`Downloading IPFS client from ${url} to ${destinationPath}`);
  const split = url.split('/');
  const fileName = split[split.length - 1];
  const archivePath = path.join(destinationPath, fileName);
  const file = await downloadWithRetry(url);
  fs.ensureDirSync(destinationPath);
  await fs.writeFile(archivePath, file);
  console.log(`Downloaded archive to ${archivePath}`);
  console.log(`Extracting ${archivePath} to ${destinationPath}`);
  try {
    await decompress(archivePath, destinationPath);
    console.log('Decompression complete');
  } catch (err) {
    console.error('Error during decompression:', err);
    throw err;
  }
  const extractedPath = path.join(destinationPath, 'kubo');
  const extractedBinPath = path.join(extractedPath, binName);
  console.log(`Moving binary from ${extractedBinPath} to ${binPath}`);
  fs.moveSync(extractedBinPath, binPath);
  console.log('Binary moved');
  console.log('Cleaning up temporary files');
  fs.removeSync(archivePath);
  console.log('Cleanup complete');
};

export const downloadIpfsClients = async () => {
  const platform = process.platform;
  console.log(`Starting IPFS client download for platform: ${platform}, targetArch: ${resolveBuildArch()}`);
  const url = getKuboUrl(platform);
  if (platform === 'win32') {
    await downloadAndExtract(url, ipfsClientWindowsPath);
  } else if (platform === 'darwin') {
    await downloadAndExtract(url, ipfsClientMacPath);
  } else if (platform === 'linux') {
    await downloadAndExtract(url, ipfsClientLinuxPath);
  } else {
    console.warn(`Unknown platform: ${platform}, defaulting to linux path`);
    await downloadAndExtract(url, ipfsClientLinuxPath);
  }
};
