
const requestIp = require('request-ip');
const {cleanURL,publicOrPrivateApi ,getJwt ,verifyToken} = require("../utilities/utils")
const {createProxyMiddleware} = require('http-proxy-middleware')
var _ = require("lodash");
var cleanDeep=require("clean-deep")
const {api} =require('../api/prisma');
const moment = require('moment');
const {sessionJwt,cacheKeys} =require('../cache')

function loadApiMiddleware(config){
    var app = config.app
    var apiURL = config.routes.api+'/:model'


  app.use('*',(req,res,next)=>{ // IP
       //// Set Client IP
       const clientIp = requestIp.getClientIp(req); 
       req.clientIp = clientIp
       next()
  })

  app.use("*",async(req,res,next)=>{ // SECOND API MIDDLEWARE
    var token = null  
      if(req.headers.authorization && req.headers.authorization.split('=')[0] === 'AUTHSSID'){
      token = req.headers.authorization.split('=')[1];
     } 



   if(token){

   var realTokenTTL =  await cacheKeys.findOneAsync({_id:token})
  // console.log(realTokenTTL)
   token = realTokenTTL?.jwt ||null

   return await verifyToken(token,config).then(e=>{
       // set jwt info 
       // set user inf
      // console.log(e) {auth,isUsers,isRoot,authModel,user,id,clientIP,authModel,role}
       req.state={
         ...req.state,
         auth:e?.auth ||false,
         user: {
           isRoot:e?.isRoot ||false,
           clientIp:req.clientIp ||'',
           authModel:e?.authModel||null,
           role:e?.role||null,
           ...e?.user,
         }
       }

       
       if(e?.auth && e?.isRoot){ // If isRoot
         return next()
       } 

       if(e?.auth && !e.isRoot){ // Not root load permissions
         return next()
       }    
      }).catch(err=>{
     
       return res.status(400).send({
         message:'Expired token',
         error:err
       })
      
      })
   }

   return next()
 })
  
   //////////////////////////////// 
   app.use(apiURL,(req,res,next)=>{ // API MIDDLEWARE

    //// Set ressource 
    req.state={
      ...req.state,
      ressource:req.params?.model,
    }

    //// set action
    var actions ={
      groupBy:req.query.groupBy,
      auth:req.query.auth||null,
      aggregate:req.query.aggregate||null,
      findRaw:req.query.findRaw||null,
      findRaw:req.query.findRaw||null,
      aggregateRaw:req.query.aggregateRaw||null,
      rawQuery:req.query.rawQuery||null,
      findMany:req.query.findMany||null,
      findOne:req.query.findOne||null,
      createMany:req.query.createMany||null,
      createOne:req.query.createOne||null,
      updateMany:req.query.updateMany||null,
      updateOne:req.query.updateOne||null,
      deleteMany:req.query.deleteMany||null,
      deleteOne:req.query.deleteOne||null,
      findType:req.query.findType||null,
      updateType:req.query.updateType||null
    }
    actions=cleanDeep(actions)
    req.state ={
      ...req.state,
      action:actions
    }

    next()
  })



  // SERVE PERMISSION AND ROLE
  //apiURL
  app.use(apiURL,async(req,res,next)=>{ // Fist API MIDDLEWARE
     // Public section
  // Check in public Permissions
    // Public role
    var role =config.init.rolePublic 
    var permissionsPublic = role[0].permissions ||{}
    var permissions = req.state?.user?.role?.permissions ||{}
    var flushPermissionsToLower={}
    Object.keys(permissionsPublic).map(key=>{
      flushPermissionsToLower[key.toLocaleLowerCase()]={...permissionsPublic[key]}
    })
    permissionsPublic=flushPermissionsToLower

    var ressource = req.state.ressource;
    var action = req.state.action;
    var exept = [
      'findMany',
      'findUnique',
      'findOne',
      'findType',
      'updateType',
      'createOne',
      'createMany',
      'updateOne',
      'updateMany',
      'groupBy',
      'deleteMany',
      'deleteOne',
      'rawQuery',
      'aggregateRaw',
      'aggregate',
      'findRaw'
    ]
    var exeptAction =exept.filter(e=>{
      return action[e]
    })

    if(
      action['auth'] && !exeptAction.length
     ) {
      return next()
     }

       /// Public 
       if(role.length){ 
        try{
          var arrayActions = permissionsPublic[ressource]?.actions || []
          var canDo = arrayActions.filter(e=>{
            return action[e]
          })
          if(canDo.length) return next() // Permission exist
        } catch(err){
        }

        // raw query
        if(ressource=='_query_'){
          var goToNext =[]  // if ressource is query & Public role
          var data =req.body?.raws || null
          Object.keys(data).map((key)=>{
          let element = data[key]
          var arrayActions = permissionsPublic[element.model]?.actions || []
          var canDo = arrayActions.filter(e=>{
            return action[e]
          })
          if(!canDo.length) goToNext.push(element)
         })
         if(!goToNext.length) return next()
        }
      }

   
   

  // If end user is authenticated
   if(req.state?.auth){
    if(req.state.user.isRoot) return next() // If Root All privileges are done , else
      /*********** */
  
      try{
        var arrayActions = permissions[ressource]?.actions || []
        var canDo = arrayActions.filter(e=>{
          return action[e]
        })
        if(canDo.length) return next() // Permission exist
      } catch(err){
      }

      // if query 
      if(ressource=='_query_'){
        let goToNext =[]  // if ressource is query & Public role
        let data =req.body?.raws || null
        Object.keys(data).map((key)=>{
        let element = data[key]
        var arrayActions = permissions[element.model]?.actions || []
        var canDo = arrayActions.filter(e=>{
          return action[e]
        })

        if(!canDo.length) goToNext.push(element)
       })

       if(!goToNext.length) return next()
       }
      /*********** */
    } 

  
    
    

   if(!api.isSet(ressource)){
    return res.status(404).send({
      message:`(404) Resource [${ressource}] not found`,
    })
   }
    
    return res.status(401).send({
      message:`Unauthorized to perform [${Object?.keys(action)?.at(0)}] on  [${ressource}]`,
    })
  })

}



module.exports = {
loadApiMiddleware
};
