
"use strict";

const express = require('express'),
  morgan = require('morgan'),
  cookieParser = require('cookie-parser'),
  bodyParser = require('body-parser'),
  http = require('http'),
  config = require('config'),
  app = express(),
  { timestamp } = require('./timestamp'),
  fs = require('fs'),
  util = require('util'),
  mkdir = util.promisify(fs.mkdir),
  unlink = util.promisify(fs.unlink),
  path = require('path'),
  fetch = require('node-fetch'),
  Promise = require('bluebird'),
  url = require('url'),
  { exec } = require('child_process');

fetch.Promise = Promise;

const basePath = "/" + config.get("http.base").replace(/^\/*/, "").replace(/\/*$/, "") + "/",
  dataPath = "/" + config.get("dataPath").replace(/^\/*/, "").replace(/\/*$/, "") + "/";
/* */

if (!config.has("auth.idsid") || !config.has("auth.password")) {
  console.error("You need to provide credentials to connect to ubit-gfx in config/local.json");
  console.error('  "auth": { "idsid": "USERNAME", "password": "PASSWORD" }');
  process.exit(-1);
}

app.use(morgan('common'));

app.use(bodyParser.json({
  verify: function(req,res,buf) {
    req.rawBody = buf;
  }
}));

app.use(bodyParser.urlencoded({
  extended: false
}));
app.use(cookieParser());

/* Routes:
 * /api/v1/publish  Publish content to repository
 */

app.get("/*", (req, res, next) => {
  /* */
  return res.status(400).send({ usage: `POST ${basePath}api/v1/publish/:distro/:releaseStream/:url` });
});

const auth = new Buffer(config.get("auth.idsid") + ":" + config.get("auth.password"), 'ascii').toString('base64');

app.post(basePath + 'api/v1/publish/:distro/:releaseStream/:url', function (req, res, next) {
  const distro = req.params.distro,
    releaseStream = req.params.releaseStream,
    remoteUrl = req.params.url;
  let filename;

  try {
    filename = path.basename(url.parse(remoteUrl).pathname);
  } catch (error) {
    return res.status(400).send({ error: `Unparsable URL: ${remoteUrl}` });
  }

  if (distro.match(/\//) || releaseStream.match(/\//)) {
    return res.status(400).send({ error: "Neither distro nor releaseStream may contain '/'" });
  }

  console.log(`POST publish/${distro}-${releaseStream}/${filename}`);

  const filepath = `${dataPath}${distro}-${releaseStream}`;

  return mkdir(filepath, { recursive: true }, () => {
    const pathname = `${filepath}/${filename}`;
    if (fs.existsSync(pathname)) {
      return res.status(409).send({ message: `'${distro}-${releaseStream}/${filename}' already exists.` });
    }

    return fetch(remoteUrl, {
      method: "GET",
      headers: {
        'Authorization': `Basic ${auth}`
      }
    }).then(result => {
      const dest = fs.createWriteStream(pathname);
      dest.on('finish', () => {
        exec(`./update-repository.sh ${distro}-${releaseStream}`, {
          cwd: ".." ,
          shell: "/bin/bash"
        }, (error, stdout, stderr) => {
          if (error) {
            return unlink(pathname).catch(() => {
              console.error(`Unable to remove ${pathname} after update-repository.sh failed.`);
            }).then(() => {
              return res.status(500).send({ message: "Error while updating aptly database.", error: error, stderr: stderr, stdout: stdout });
            });
          }
          return res.status(200).send({ message: "OK", stdout: stdout || "", stderr: stderr || "" });
        });
      });
      result.body.pipe(dest);
    }).catch((error )=> {
      const message = `Unable to download ${remoteUrl}: ${error}`;
      console.error(message);
      return res.status(500).send({ message: message });
    });
  }).catch((error) => {
    const message = `Unable to mkdir ${filepath}: ${error}`;
    console.error(message);
    return res.status(500).send({ message: message });
  });
});

app.post("/*", (req, res, next) => {
  /* */
  return res.status(400).send({ usage: `POST /${basePath}/api/v1/publish/:distro/:releaseStream/:url` });
});


const server = http.createServer(app),
  port = config.has("port") ? config.get("port") : 6543;

server.listen(port);
server.on('listening', function() {
  let addr = server.address();
  let bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  console.log(timestamp() + ` Now serving ${basePath} on ${bind}`);
});

module.exports = server;
