const mappers = {
  String: (t)=>{
    if(typeof t !== 'string'){
      throw new Error( '`' + t + '` is not a string' )
    }
    return t;
  },
  Boolean: (t)=>{
    if('1,true,yes,yep,y'.split(',').indexOf(t)>-1)
      return true;
    if('0,false,n,no,nope,null'.split(',').indexOf(t)>-1)
      return false;

    if(typeof t !== 'boolean'){
      throw new Error( '`' + t + '` is not boolean' )
    }
    return t;
  },
  Number: (t)=>{
    if(typeof t !== 'number'){
      let x= parseFloat(t)
      if(x==t)
        return x;

      throw new Error( '`' + t + '` is not a number' )
    }
    return t;
  },
  Array: (t)=>{
    try{
      let res = JSON.parse(t);
      if(!Array.isArray(res))
        throw new Error('`'+t+'` is not an Array')
      return res;
    }catch( e ){
      throw new Error( '`' + t + '` is not an Array' )

    }
  }

};
const parseArgs = function(req, res, opts) {
  const query = Object.assign({},req.params,req.query);
  const args = {};
  const errs = [];
  for(let k in opts) if(opts.hasOwnProperty(k)){
    if( opts[ k ].required ){
      if( !( k in query ) ){
        errs.push( `Required argument ${k} (${opts[ k ].type.name}) is not specified` );
        continue;
      }


      if( query[ k ] === null || query[ k ] === void 0 ){
        if( opts[ k ].empty ){
          args[ k ] = query[ k ];
          continue
        }else{
          errs.push( `Argument ${k} (${opts[ k ].type.name}) can not be empty` );
          continue
        }
      }
    }
    if( query[ k ] === null || query[ k ] === void 0 ){
      args[ k ] = query[ k ];
      continue
    }
    try{
      args[ k ] = mappers[ opts[ k ].type.name ]( query[ k ] )
    }catch( e ){
      errs.push( `Argument \`${k}\` (${opts[ k ].type.name}) type mismatch: ${e.message}` );
    }
  }
  if(errs.length)
    throw errs;

  return args;
};
var fs = require('fs');
var logStream = fs.createWriteStream("/tmp/kus-quizard.txt", {flags:'a'});
function getDate(date){
  if(date instanceof Date) {
    return {
      day: date.getDate(),
      month: date.getMonth(),
      //monthName: months[date.getMonth()],
      year: date.getFullYear(),
      date: date
    };
  }else if(date === null || date === void 0){
    return null;
  }else{
    return date;
  }
}
const months = [
  'January',
  'February',
  'March',
  'April',
  'May',
  'June',
  'Jule',
  'August',
  'September',
  'October',
  'November',
  'December'
];
const shortMonths = months.map(function(name) {
  return name.substr(0,3);
});
shortMonths[8] = 'Sept';
function humanDate(date) {
  let d = getDate(date);
  return [d.day, shortMonths[d.month], d.year].join(' ');
}


module.exports = {
  api2html: function(apis) {
    var out = [];
    for(let key in apis){
      let api = apis[key];

      out.push(`<div class="api"><H2>${api.method} ${key}</H2>
<div class="api-description">${api.summary}</div>

<div class="api-options"><span class="api-options-title">Options:</span>
${Object.keys(api.options).map(optName=>{
        let opt = api.options[optName];
        return `<div class="api-option">
<span class="api-option-name">${optName}</span>
<span class="api-option-type">${opt.type.name || opt.type}</span>
${opt.required?'<span class="api-option-required">Required</span>': '<span class="api-option-optional">Optional</span>'}
&nbsp;— <span class="api-option-description">${opt.description}</span>
</div>`;
      }).join('')}
</div>

</div>`)
    }
    return out.join('')
  },
  api2routes: function(apis, router) {
    for(let key in apis){
      let api = apis[key], args;
      router[api.method.toLowerCase()](key, async function(req, res) {
        let timeout = setTimeout(()=>{
          res.status(408);
          res.end('{"error": true, "data": "Timeout"}');
          try{
            res.connection.destroy();
          }catch(e){

          }
        }, 30000);
        try{
          args = parseArgs( req, res, api.options );
        }catch(e){

          return res.end('Errors:\n' +e.map(e=>'\t'+e).join('\n'))
        }

        if(req.method === 'POST'){
          try{
            args.body = await new Promise( function( r, j ){
              var body = '';

              req.on('data', function (data) {
                body += data;

                // Too much POST data, kill the connection!
                // 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB
                if (body.length > 1e6){
                  req.connection.destroy();
                  j();
                }
              });

              req.on('end', function () {
                r(JSON.parse(body));
              });
            } )
          }catch(e){
            args.body = null;
          }
        }

        console.log(api.method, key, JSON.stringify(args));

        res.header("Content-Type", "application/json; charset=utf-8");

        try{
          let result = await api.fn(args, req, res);
          // fs.writeFileSync('./data/tmp/full.json', JSON.stringify(data, null, 2))
          logStream.write(`${humanDate(new Date())} ${api.method}: ${key} ${JSON.stringify(args)}`)
          //logStream.write(`${JSON.stringify(result.log, null,1)}\n`);

          res.end(typeof result === 'string' ? result : JSON.stringify(result, null,1), 'utf-8');
          clearTimeout(timeout)
        }catch(e){
          debugger
          logStream.write(`${humanDate(new Date())} ${api.method}: ${key} ${JSON.stringify(args)}`)
          logStream.write(`ERROR: ${e.message+'\n'+e.stack}\n\n`)

          res.end(e.message+'\n'+e.stack)
        }
      })
    }
  }
};