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){
        res._log('!', err)
        cache[ '/' + cacheEntry  ] = JSON.stringify(err, null,2);
      }else
        cache['/'+cacheEntry] = result.code;
    });

  });
};

var path = require('path');

var bCore = require( "@babel/core" );
var simpleTransformToAMD = require('./pack/babel-plugin-transform-2015es-to-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
        } ],
        //[simpleTransformToAMD]
        ['@babel/plugin-transform-modules-amd']
      ],
      sourceMaps: 'both',
      sourceFileName: fileName,
      moduleId: fileName
    }, function( c, d, e ){
      if(c){
        cb(c);

      }else{
        cb(false, d);
      }
    } );
};
/*
var transformTSX = function(code, fileName, cb) {
  bCore.transform(
    `import "main.ts";const x:number = 4;
    
    export const trata = 2 as number;
    `,//code,
    {
      "plugins": [
        ['@babel/plugin-transform-typescript', {
          isTSX: true,
          jsxPragma: 'D'
      }],
        /!*[ "@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
        } ],*!/
        [simpleTransformToAMD]
      ],
      sourceMaps: 'both',
      sourceFileName: fileName,
      moduleId: fileName
    }, function( c, d, e ){
      console.log(c)
      console.log(d.code)

      debugger
      if(c){
        cb(c);

      }else{
        cb(false, d);
      }
    } );
};*/
//transformTSX('', 'aga', ()=>{})
var sass = require('node-sass');
var transformServe = function(dir, anything) {
  let outerFixedUrl;

  let serve = function (req, res, next) {
    var qRes = res;
    let fixedUrl;
    if (req.url in cache) {
      return res.end(cache[req.url])
    }
    if(req.url === '/global-styles/base.scss')
      debugger
    if(!anything){
      fixedUrl = req.url;
    }else{
      fixedUrl = arguments[3];
    }

    if (fixedUrl.substr(-5) === '.scss'){
// not secure
      //console.log('scss', dir, req.url, __dirname)
      fs.readFile( path.join( __dirname, dir, fixedUrl ), function( err, data ){
        if( err ){
          next();
        }else{
          if(pack && pack.scss && pack.scss.shared){
            data = pack.scss.shared+';\n'+data;
          }
          sass.render( {
            data: data+'',
            file: path.join( __dirname, dir, req.url ),
            sourceMap: true,
            importer: function( url, prev, done ){
              if(url[0] === '/'){
                url = path.join( __dirname, dir, url.substr(1));
              }
              var displayName = path.relative( path.join( __dirname, dir ), path.resolve( path.dirname( prev ), url ) )
              var name = path.resolve( path.dirname( prev ), url );


              let fName;
              let contents;
              try{
                contents = fs.readFileSync( name ) + '';
                done( {
                  file: displayName, // only one of them is required, see section Special Behaviours.
                  contents: contents
                } );
                return;
              }catch(e){}
              res._log('Resolve scss dep ', url, 'for', fixedUrl);

              try{
                contents = fs.readFileSync( name+'.scss' ) + '';
                done( {
                  file: displayName, // only one of them is required, see section Special Behaviours.
                  contents: contents
                } );
                return;
              }catch(e){}

              try{
                contents = fs.readFileSync( name+'.css' ) + '';
                done( {
                  file: displayName, // only one of them is required, see section Special Behaviours.
                  contents: contents
                } );
                return;
              }catch(e){}

              done(new Error('Can not resolve dependency!'))

            }
          }, function( err, result ){
            if( err ){
              const errorText = `Error at ${err.file}:\n` + err.formatted;
              return res.end( errorText )
            }
            res.set( 'Content-type', 'text/css; charset=UTF-8' );
            res.end( result.css )
          } );
        }
      } );
    }else if (fixedUrl.substr(-4) === '.jsx') {

      fs.readFile(path.join(dir, fixedUrl), function(err, data){
        if( err ){
          next();
        }else{
          res._log('Transform jsx');
          transformJSX(data+'', req.url, function(err, result) {
            if(err){
              res._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.set( 'Content-type', 'application/javascript; charset=UTF-8' );
              res.end( result.code );
            }
          })
        }
      });
    }/*else if (req.url.substr(-4) === '.tsx' || req.url.substr(-3) === '.ts') {

      fs.readFile(path.join(dir, req.url), function(err, data){
        if( err ){
          next();
        }else{
          console.log('Transform ts/tsx', req.url);
          transformTSX(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.header( 'Content-type', 'application/javascript; charset=UTF-8' );
              res.end( result.code );
            }
          })
        }
      });
    }*/else if (fixedUrl.substr(-4) === '.svg') {
      fs.readFile(path.join(dir, fixedUrl), function(err, data){
        if( err ){
          next();
        }else{
          res._log('Generate SVG declaration');

          bCore.transform(
            `const SVG = D.declare("${req.url.replace(/\//g,'.').split('.').filter(String).join('.')}", (cfg)=>{ return ${(data+'').replace(/(<svg[^>]+"\s*)>/i,'$1 {...cfg}>')}; });
            export default SVG;
            `,
            {
              "plugins": [
                [ "@babel/plugin-transform-react-jsx", {
                  "pragma": "D.s", // default pragma is React.createElement
                  "throwIfNamespace": false // defaults to true
                } ],
                ['@babel/plugin-transform-modules-amd']
              ],
              sourceMaps: 'both',
              sourceFileName: req.url,
              moduleId: req.url
            }, function( err, result ){
              if(err){
                res._log('!Error in transforming svg', err);
                res.end(err.message+'\n'+err.stack)
              }else{
                cache[ req.url + '.map' ] = JSON.stringify( result.map );
                res.set( 'SourceMap', req.url + '.map' );
                res.header( 'Content-Type', 'text/javascript' );
                res.end( result.code );
              }
            } );
        }
      });

    } else {
      next();
    }
  };
  if(!anything)
    return function(req, res, next) {
      return serve(req, res, next);
    };


  return function(req, res, next) {
    //return next();
    if(req.url.substr(-3)[0] === '.' || req.url.substr(-4)[0] === '.')
      return next();

    let types = 'jsx,scss,svg'.split(',')
    let test = 0;
    let iterate = function() {
      let outerFixedUrl = req.url+'.'+types[test];
      serve(req, res, function(a,b,c) {
        if(test<types.length){
          res._log('try serve', outerFixedUrl);
          test++;
          iterate();
        }else{
          next();
          test = 0;
        }
      }, outerFixedUrl);
    };
    iterate();
  };
};

var count = 0;
var list = [];
var pad = function(n) {
  return n<10 ? '0'+n: n+'';
};
var getLogItem = function(logItem) {
  if( logItem[ 0 ] === '!' ){
    return {data: [ '\t' ].concat( logItem.substr( 1 ) ), error: true };
  }else if( logItem[ 0 ][ 0 ] === '!' ){
    return {data: [ '\t', logItem[ 0 ].substr( 1 ) ].concat( logItem.slice( 1 ) ), error: true };
  }else{
    return {data: [ '\t' ].concat( logItem ), error: false };
  }
};
app.use(function(req, res, next) {

  var d = new Date();
  res.on('finish', function() {
    // Do log;
    if(log.length){
      let udishen = '';
      if(log.length === 1){
        udishen = getLogItem(log[0]).data;
        console[udishen.error?'error':'log'].apply(console, [
          d.getHours() + ':' + pad( d.getMinutes() ) + ':' + pad( d.getSeconds() ),
          'Request', req.url, '\t→ '
        ].concat(udishen) )
      }else{
        console.log.apply(console, [
          d.getHours() + ':' + pad( d.getMinutes() ) + ':' + pad( d.getSeconds() ),
          'Request', req.url
        ]);
        log.forEach( function( logItem ){
          var item = getLogItem(logItem);;
          if(item.error){
            console.error.apply(console, item.data);
          }else{
            console.log.apply(console, item.data);
          }

        } )
      }

    }
  });
  let log = [];
  res._log = function() {
    log.push([...arguments]);
  };



  next();
});

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

app.use(transformServe('src', true));

app.use(function(req, res, next) {
  res._log('!Not serving', req.url);
});

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{
      if(filename.substr(-4)==='.jsx'){
        var code = await readFile( filename ) + '';
        var url = '/' + path.relative( './src', filename ).replace( /\\/g, '/' )
        console.log( 'Live transform jsx ', url )
        var result = await transformJSXPromised( code, url );
        files.push( { file: url, 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);
