'use strict';
require("dotenv").config();
const workerpool =require('workerpool')
const task =require("node-cron")
const {setFeature}=require('./features') // Set all feature
const {Initialize,WatchInitialize,InitializeWatchStream,InitializeCache} = require('./init')
const utils = require("./utilities/utils")




const {validator} = require('./validator/index')




// Gblabl set
global.coreConfig={};




//
const { v4: uuidv4 } = require('uuid');
const cookieSession = require('cookie-session')
const compression = require('compression')
const {loadmodelviews} = require("./modeviews") // Loadmodelviews
const {captureInternalError} =require('./process')
const { config: initConfig } = require("./types/index");
const {useAll} = require('./app-use/index')
const express = require("express");
const boolParser = require('express-query-boolean');
const autoParser = require('./transform/index'); // Auto Parsing
const {initRedisCache} =require('./cache')
//const intParser = require('express-query-int');
const package_ = require("./package.json")
const {initMongo} = require('./mongo-connector')

const cors = require("cors");
const _ =require("lodash")

const app = express();
const cache = require('express-cache-ctrl');
const {updateProject} = require('./script/update')

const {autoReload} = require('./vite')
const http = require("http");
const server = http.createServer(app);

const consola = require("consola");
const colors = require("colors");
global.cron = require("node-cron");
global.glob = require("glob");
global.consola = consola;
const cleanDeep = require("clean-deep");

// require all
const { loadAuth } = require("./auth"); //AUTH
const { loadPlugins } = require("./plugins"); //  PLUGINS
const { loadHooks } = require("./hooks"); // HOOKS
const { loadExpress, startApp } = require("./server/app"); // APP ROUTE EXPRESS
const { loadEndpoints } = require("./endpoints"); // EXPRESS
const { loadServices } = require("./services"); // SERVICES
const { api, autoUpdateDatabase, prismaMiddleware ,loadExtendPrisma,logEvents,validationSchema } = require("./api/prisma"); // PRISMA API
const { loadTasks } = require("./tasks"); // TASK CRON
const { loadModels } = require("./models"); // MODELS
const {setSocket } = require("./socket/io")
const {loadWebsocket} = require('./socket/')
const {loadCollection} =require("./collections")
const {restartServer,createRootFolder} = require('./utilities/utils')



const {
  onBoot,
  onRegister,
  onReady,
} = require("./providers"); // Providers app

const { loadAdminPanel } = require("./admin/index"); // Admin panel utilis
const {  loadApiMiddleware } = require("./server/useExpressMiddleware"); // Load permissions API

const { loadAllMiddlewareFile } = require("./server/middlewares__");
const {loadPoliciesAPI} = require('./policies')

// upload file
const { loaduploadExpress } = require("./upload");
//const {syncApps}= require("./apps")


// get databaseConfig
const {setDataBaseProvider} = require("./database/index.js")

class Bitev {
  constructor(config = {}) {
    this.api = api;
    this.config = config;
    this.hooks = {};
    this.initConfig = initConfig(app);
  }
 
  async init(config = this.initConfig) {
   
    config = {
      uuid:null,
      package:package_,
      ...this.initConfig,
      ...cleanDeep(config),
      upload:{
        ...this.initConfig.upload,
        ...cleanDeep(config.upload)
      },
      express: {
        ...cleanDeep(config.express),
        limit:{
          ...this.initConfig.express.limit,
          ...cleanDeep(config.express.limit)

        },
        corsOptions: {
          ...this.initConfig.express.corsOptions,
          ...cleanDeep(config.express.corsOptions),
        },
      },
      api: this.api, // Prisma instance
      service: {
        
      },
      server: server,
      state: {
        ...this.initConfig.state,
        ...cleanDeep(config.state)
      },
      collections:{
        schemas:[],
        models:{},
      },
      roles:[],
      policiesAPI:{
        
      },
      caches:{},
      cachesConfig:[],
      hooks: {},
      middlewares: {},
      apiProviders: [],
      modelProviders: {},
      uploadDir:''
    };

// Initernal function
/**
 * getOneCollection
 * @param {*} model 
 * @returns 
 */
config.getOneCollection = (model)=>{ // get One collection  
  var collection =config.collections.schemas.filter(col=>{
    return col.slug.toLowerCase() === model.toLowerCase()
  })
  if(collection.length) return collection[0]
  return null
}

config.getFieldsCollection= (model)=>{ // get One collection  
  var collection =config.collections.schemas.filter(col=>{
    return col.slug.toLowerCase() === model.toLowerCase()
  })
  if(collection.length) return collection[0].fields ||[]
  return null
}

config.getSelectFields = (model)=>{ // get One collection  
  var collection =config.collections.schemas.filter(col=>{
    return col.slug.toLowerCase() === model.toLowerCase()
  })
  var select ={
    id:true
  }
  if(collection.length) {
    collection[0].fields.map(f=>{
      if(!f.type.match(/(relationship|computed)/) && !f?.private){
        select[f.name]=true
      }
    })
  }
  return select
}
config.getPrivateFields = (model)=>{ // get One collection  
  var collection =config.collections.schemas.filter(col=>{
    return col.slug.toLowerCase() === model.toLowerCase()
  })
  var select =[]
  if(collection.length) {
    collection[0].fields.map(f=>{
      if(f?.private){
        select.push(f.name)
      }
    })
  }
  return select
}
config.getIncludeFields = (model)=>{ // get One collection  
  var collection =config.collections.schemas.filter(col=>{
    return col.slug.toLowerCase() === model.toLowerCase()
  })
  var select ={
    id:true
  }
  if(collection.length) {
    collection[0].fields.map(f=>{
      if(f.type.match(/(relationship)/)){
        select[f.name]=true
      }
    })
  }
  return select
}
config.privateFields =(obj,fields)=>_.omit(obj,fields)
config.formatValue =(model,field,value)=>{ // Format value
try{

  var col = config.getOneCollection(model)
  var field = col.fields.filter(f=>{
    return f.name.toLowerCase() === field.toLowerCase()
  })

 if(field.length){
  if(field[0].type.toLowerCase().match(/(string|textarea|select)/)){
    return String(value)
  }

  if(field[0].type.toLowerCase().match(/(number|float|int|integer)/)){
    return Number(value)
  }
 } 

 return value

} catch(err){
console.log(err)
return value


}

}

// sync all features : collection

await setFeature(config)
await Initialize(config)
if(config.redis.cache){
  await initRedisCache(config)
}
WatchInitialize(config)



    

    // set Provider  Database
    setDataBaseProvider(config)

      // If server need to restart
    restartServer()

    // First thing
    //updateProject()

    // autoReload
    autoReload()



    // Load Socket IO
    await setSocket(config)
    // Init Mongodb
    initMongo(config)

    // emit internal Erros
    captureInternalError(config)

    

    // sync root folder requirements
    //await createRootFolder()


    // load Config
    await loadCollection(config)

    

    // load models
    await loadModels(config)

    

    // load extendPrisma
    await loadExtendPrisma(config)
    logEvents(config)




    //useAll(app) // use all middle<are


    //console.log("cors",config.express.corsOptions)
    // Set Base expresss
    app.use(cookieSession({
      name: 'session',
      keys: [uuidv4()]
    }))
    app.use(compression())
    app.use(cors(config.express.corsOptions));
    app.use(express.json({limit:config.express.limit.jsonSize}));
    app.use(express.text({limit:config.express.limit.textSize}));
    app.use(express.urlencoded({ extended: true }));
    app.use(boolParser());
    app.use(autoParser());
    
  // app.use(queryType.middleware())
    app.use('*',cache.secure())

 
   
  

    // Init Provider
    onBoot(config); //
  
    await onRegister(config); // register own binding state
    
    await loadAuth(config);
    await loadServices(config);
    await loaduploadExpress(config);

    await loadPlugins(config); // load plugins
    await loadPoliciesAPI(config); // load policies API
    await loadHooks(config); // load hooks
    await loadApiMiddleware(config); // Load express middleware
    await loadExpress(config, app); // load App:app (Express) and init routes for rest API
   
    loadTasks(config);
    await prismaMiddleware(config);
    await loadAllMiddlewareFile(config);
    await loadmodelviews(config)
    await loadEndpoints(config); // load endpoint now
     // Sync App modules
    // await syncApps()


    // Initalize caches
    await InitializeCache(config)
    

    // Watch stream and sync all
    InitializeWatchStream(config)

    // loadWebsocket() Load socket folder
     await loadWebsocket()

    // Load server admin panel
    loadAdminPanel(config, app);

    startApp(config);
    onReady(config); // When is ready

    



    if (process.env.NODE_ENV == "dev") {
      await autoUpdateDatabase(true,config); // Prisma : Update database & sync
    } else {
      autoUpdateDatabase(false,config); // lock Prisma : Update database & sync
    }

    // Apply global config
    /**
     * 
     */
   // global.coreConfig = config;

  }
}


Bitev.utils = utils 
Bitev.api = api
Bitev._ = _
Bitev.workerpool = workerpool
Bitev.task = task



module.exports = Bitev;

