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;
  }

};
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;
};
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"}');
          res.connection.destroy();
        }, 10000);
        try{
          args = parseArgs( req, res, api.options );
        }catch(e){

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

        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);

          res.end(typeof result === 'string' ? result : JSON.stringify(result, null,1), 'utf-8');
          clearTimeout(timeout)
        }catch(e){
          debugger
          res.end(e.message+'\n'+e.stack)
        }
      })
    }
  }
};