var env = process.env;

const APP_HOST = env.APP_HOST || "127.0.0.1";
const APP_PORT = env.APP_PORT || 4000;
const DB_PATH = env.DB_PATH || "./db/users.json";

const App = require('express');
const Router = require('node-async-router');

const app = App(),
      router = Router();

const pack = require('./pack');

const dir = function(...args) {
  return path.join.apply(path, [__dirname, ...args])
};

const fs = require( 'fs' );

var cache = {};


const serveBundle = async function(tpl, res){
  var fileName = dir(pack.src, pack.entry.js);
  var posixName = pack.entry.js;//path.posix.join(pack.src, );
  console.log(fileName)
  fs.readFile(fileName, function(err, jsx) {
    if(err){
      return res.end(err.message);
    }
    const cacheEntry = posixName;//+new Date()+'_'+Math.random().toString(36).substr(2)
    res.end(tpl
      //.replace('$DATA$', JSON.stringify(data))
      .replace('$BUNDLE$',
        '<script src="'+cacheEntry+'"></script>')
    );

    transformJSX(jsx+'', pack.entry.js, function(err, result) {
      if(err){
        console.error(err)
        cache[ '/' + cacheEntry  ] = JSON.stringify(err, null,2);
      }else
        cache['/'+cacheEntry] = result.code;
    });

  });
};

var path = require('path');

var bCore = require( "@babel/core" );
var b = require('@babel/plugin-transform-modules-amd')
var transformJSX = function(code, fileName, cb) {
  bCore.transform(
    code,
    {
      "plugins": [
        [ "@babel/plugin-transform-react-jsx", {
          "pragma": "D.h", // default pragma is React.createElement
          "pragmaFrag": "D.f", // default is React.Fragment
          "throwIfNamespace": false // defaults to true
        } ],
        ['@babel/plugin-transform-modules-amd']
/*        ["@babel/plugin-transform-modules-commonjs", { "synchronousImport": true }],
        ['func-wrap', {
          /!* use a named export *!/
          name: 'library',

          /!* assign arguments to the function *!/
          args: ['fileName="'+fileName+'"'],

          /!* export as CommonJS *!/
          format: 'cjs'
        }]*/
      ],
      sourceMaps: 'both',
      sourceFileName: fileName,
      moduleId: fileName
    }, function( c, d, e ){
      if(c){
        cb(c);

      }else{
        cb(false, d);
      }
    } );
};
var sass = require('node-sass');
var transformServe = function(dir) {
  return function (req, res, next) {
    if (req.url in cache) {
      return res.end(cache[req.url])
    }
    /*if (req.url.substr(-8) === '.jsx.map') {
      debugger
    }*/

    if (req.url.substr(-5) === '.scss') {
// not secure
      //console.log('scss', dir, req.url, __dirname)
      fs.readFile(path.join(__dirname,dir,req.url), function(err, data){
        if( err ){
          next();
        }else{
          sass.render({
            file: path.join(__dirname,dir,req.url),
            sourceMap: true,
            importer: function(url, prev, done) {
              //console.log(url, prev, done)
              // url is the path in import as is, which LibSass encountered.
              // prev is the previously resolved path.
              // done is an optional callback, either consume it or return value synchronously.
              // this.options contains this options hash
              setTimeout(function(result){
                var name = path.resolve(path.dirname(prev), url);
                var displayName = path.relative(path.join(__dirname,dir),path.resolve(path.dirname(prev), url))

                done({
                  file: displayName, // only one of them is required, see section Special Behaviours.
                  contents: fs.readFileSync(name)+''

                });
                //console.log({name, __dirname, dir, url, prev})
              },10);
              // OR
              //var result = someSyncFunction(url, prev);
              //return {file: result.path, contents: result.data};
            }
        }, function(err, result) {
            if(err){
              const errorText = `Error at ${err.file}:\n`+err.formatted;
              return res.end(errorText)
            }
            res.header('Content-Type', 'text/css');
            res.end(result.css)
            //debugger
            /*...*/ });
        }
      });
    }else if (req.url.substr(-4) === '.jsx') {
      console.log('Serve jsx', dir, req.url)
      fs.readFile(path.join(dir, req.url), function(err, data){
        if( err ){

          next();
        }else{
          console.log('Transform jsx', req.url);
          transformJSX(data+'', req.url, function(err, result) {
            if(err){
              console.log('Error in transforming jsx', err);

              res.end(err.message+'\n'+err.stack)
            }else{
              cache[ req.url + '.map' ] = JSON.stringify( result.map );
              res.set( 'SourceMap', req.url + '.map' );
              res.end( result.code );
            }
          })
        }
      });

    } else {
      next();
    }
  }
};

app.use(transformServe('public'));
app.use(transformServe('src'));
var lives = [];
app.use('/', function(req, res, next){
  console.log( req.originalUrl )
  if( req.originalUrl === '/' ){
    fs.readFile( dir( pack.src, pack.entry.html ), function( err, data ){
      if( err ){
        res.end( JSON.stringify( err, null, 2 ) );
      }else{
        serveBundle( data + '', res );

      }
    } );

  }else if( req.originalUrl === '/[live]' ){
    lives.push( res );
  }else if( req.originalUrl in cache ){
    res( cache[ req.originalUrl ] );
  }else{
    next();
  }
});
app.use(App.static(dir(pack.public)));

app.use(App.static(dir(pack.src)))




const util = require('util');
const readFile = util.promisify(fs.readFile);
const transformJSXPromised = util.promisify(transformJSX);

let debounce = {};
let shouldUpdate = false;
var doUpdate = async function(){

  var files = [];
  for( let filename in debounce ){
    try{
      var code = await readFile( filename ) + '';
      var result = await transformJSXPromised( code, path.relative( './public', filename ) );
      files.push( { file: '/'+path.relative( './public', filename ).replace(/\\/g, '/'), content: result.code } );
    }catch( e ){

      console.log( 'Error in ' + e );
    }
    delete debounce[filename];

  }
  var live, liveUpdate = JSON.stringify(files);
  while((live = lives.pop()))
    live.end(liveUpdate);
  shouldUpdate = false;
};
let debounceUpdate = function(filename){
  if(filename.indexOf('_tmp_')===-1 && filename.indexOf('_old__')===-1){
    debounce[ filename ] = true;
    if(!shouldUpdate){
      shouldUpdate = true;
      setTimeout(doUpdate, 300);
    }
  }
};
var watch = require('recursive-watch');
watch('./src', function(filename){
  if(filename.indexOf('_tmp_')>-1)
    return;

  debounceUpdate(filename);
});

/*watch('./src', function(filename){
  if(filename.indexOf('_tmp_')>-1)
    return;

  debounceUpdate(filename);
});*/


app.listen(APP_PORT);

console.log(`LISTEN port :`+APP_PORT);
