
const { cleanPath } = require("../utilities/utils");
const {updateMediaCollection ,updateRole} =require('./update')
const fs = require("fs");
const _ =require("lodash")
const {validationSchema} = require("../api/prisma")
const {
  glob,
  globSync,
  globStream,
  globStreamSync,
  Glob,
} = require('glob')
const system = require("system-commands");
const fsx=require('fs-extra')
const {execSync} = require('child_process')
const generateUniqueId  =require("generate-unique-id")
const beautify = require('js-beautify').js
const rootCollections = [
  'Roles',
  'tokens_api',
  'Medias',
  'Settings',
  'logs',
  'configs',
  'modelviews',
  'Users',
  'Webhooks'
]



async function cleanSchema(arr){

// Format to JSON
var arrJson  ={} 
arr.map(col=>{
  arrJson[col.slug]= col
})  

// finalSchema =[]
var finalSchema=[]


_.mapKeys(arrJson,(col,key)=>{
 let fields=[]
 let _tmpFields = [...col?.fields] || []

 

 _tmpFields.map(field=>{

  let omitField = false
  if(field.type=='relationship' && field.relationType?.match(/(many-to-one|one-to-many|one-to-one|many-to-many)/)){

    //console.log(key,field.name,field.relationTo)
    omitField=true
    // Get collection linked to current field
    let ParentCol = arrJson[field?.relationTo]

    if(ParentCol){
      let ParentFields = ParentCol?.fields ||[]

      ParentFields.map(f=>{
        if(f.name==field.relationSideName){

          omitField =false
        }
      })
    }

    
  }

 
  if(!omitField) fields.push(field)
 })

 let schema = col
 col.fields=fields

 finalSchema.push(col)

})


//console.log(finalSchema)



finalSchema.map(col=>{
  // clean composite field on all
  let makeFields =[]
  var fields = col?.fields||[]
  fields.map(f=>{
    if(f.type=='composite'){
      let compositeExisit = finalSchema.find(e=>e.slug==f.composite)
      if(compositeExisit) makeFields.push(f)
    } else {
      makeFields.push(f)
    }
  })
 col.fields = makeFields

})


return new Promise((resolve, reject) => {



resolve(finalSchema)


})



}


async function loadCollection(config){

   return new Promise(async(resolve,reject)=>{
    var Path = cleanPath(process.cwd() + "/collections/*.js")
    var schemaDatabaseArray = []
    glob(Path,{},async(err,files)=>{
            files.map(file=>{
              let collection_ =require(""+file)
               if(collection_?.slug){
                var isRootCollection = rootCollections.find(e=>e.toLocaleLowerCase()==collection_.slug.toLowerCase())
                if(isRootCollection){
                  collection_._v = true
                } else {
                  collection_._v= false
                }
                schemaDatabaseArray.push(collection_)
               }
                //console.log(schemaDatabaseArray)
            })

         

            // Update collection params
            schemaDatabaseArray.map((updateCol)=>{
                updateMediaCollection(updateCol)
            })

        
      
        schemaDatabaseArray =[...new Map(schemaDatabaseArray.map(v => [v.slug, v])).values()] // Remove duplicate schema
      

       var SchemaArrayToBuild = await cleanSchema(schemaDatabaseArray)
       config.collections.schemas= SchemaArrayToBuild




        await generateSchema(SchemaArrayToBuild,config)

    })

    resolve()
   })
}


async function generateSchema(schemaDatabase,config){
  try{
    var typeField={
      name:'',
      label:'',
      type:'string',
      hasMany:false,
      required:false,
      composite:'',
      unique:false,
    }
    
    var typeSchema={
      singleType:false,
      single:false,
      label:'',
      slug:'',
      mode:'model',
      timestamps:true,
      fields:[]
    }
  var schemaFile = ``


 
  //console.log(config.databaseConfig)

  
  
  
  var setSchema =`
  // Prisma Schema auto-generated by bitev , Please dont touch anything ...
  generator client {
    provider = "prisma-client-js"
    previewFeatures=[]
  }

  generator jsonSchema {
    provider = "prisma-json-schema-generator"
   // keepRelationScalarFields = "true"
    output ="models"
    persistOriginalType="true"
  }
  
  datasource db {
    provider = "${config.databaseConfig.provider}"
    url      = env("DATABASE_URL")
  } 


  model vcaches {
    id String @id @default(auto()) @map("_id") @db.ObjectId
    name String   @unique  
    model String?     
    params Json?  
    pipeline Json?   
    action String?
    expiration Float?     
    roles String[]    
    description String?
    active Boolean? @default(true)  
    createdAt DateTime? @default(now()) 
    updatedAt DateTime? @updatedAt
  }



  `

 

  for await (var eachSchema of schemaDatabase){
    var schema ={
      ...typeSchema,
      ...eachSchema,
    }

    // delete duplicate
   schema.fields = _.uniqBy(schema.fields,'name')

    var setField = {
      value:''
    }

   
    if(schema.mode==='model'){
     
      if(config.databaseConfig.provider=='mongodb'){
        setField.value = `id String @id @default(auto()) @map("_id") @db.ObjectId`
      }
      if(config.databaseConfig.provider=='sqlite'){
        setField.value = `id Int @id @default(autoincrement())`
      }


    } 
    if(schema.mode==='composite'){
       setField.value = ``
    } 
    var timestamps = schema.timestamps ?`
      createdAt DateTime? @default(now()) 
      updatedAt DateTime? @updatedAt`:''

      var indexSet=``
      
    for await(var eachField of schema.fields){


  
      var field ={
        ...typeField,
        ...eachField
      }

      if(field.index && schema.mode=='model' && !field.unique){
        indexSet+=`@@index([${field.name}])`
      }

      if(field.type.toLowerCase().match(/(planning|calendar|computed)/)) continue
  
      // Format entry
  
      field.name = field.name.replace(/\s/g,'_').toLocaleLowerCase();
  
      var type_ ="String"
      var mode_ =schema.mode || "model"
      var relationShip_ =''
      var defaultValue= field.type.match(/(number|integer|float)/) ? Number(field.defaultValue):`"${field.defaultValue}"`
      var scalarDefaultValue = field?.scalarDefaultValue||null
      if(field.type=='enum'){
        defaultValue = field.defaultValue
      }

      // If default value is boolean
      if(field.type==='boolean') {
        defaultValue = !field?.defaultValue  ? false : field.defaultValue
        if(defaultValue==true || defaultValue=='true'){
              defaultValue=true
        }
      }
      // if defined 


    //  ______________
  
      var default_ = field.defaultValue !==undefined ? `@default(${defaultValue})`:''
      var required_ =field.required ? '':'?'
      var hasMany_ = field.hasMany || false
      var unique_ = field.unique ? '@unique':''

     // ___________________________
  
       // Has Many
       if(hasMany_) required_ ='[]'
  
      // Condition sur les types de données


      if(field.type.toLowerCase().match(/(composite)/)) type_ = field?.composite
      if(field.type.toLowerCase().match(/(enum)/)) {
        type_ = schema.slug+'_'+field.name
        setSchema+=`enum ${type_}{
            ${field.optionsEnum}
          }       `
      }
      if(field.type.toLowerCase().match(/(json|point|map|editor|media|select)/)) type_ = "Json"
      if(field.type.toLowerCase().match(/(date|datetime|datetime-local|time|colour)/)) type_ = "String"
      if(field.type.toLowerCase().match(/(number)/)) type_ = "Float"
      if(field.type.toLowerCase().match(/(integer)/)) type_ = "Int"
      if(field.type.toLowerCase().match(/(boolean)/)) type_ = "Boolean"
      if(field.type.toLowerCase().match(/(string|email|search|url|text|ip|textarea|richtext|password|random)/)) type_ = "String"
      if(field.type.toLowerCase().match(/(string)/) && field.scalar) {
        type_='String[]'
        required_=''

       

        //console.log(`@default(${JSON.stringify(field.scalarDefaultValue)})`)
       default_ = field.scalarDefaultValue ? `@default(${JSON.stringify(field.scalarDefaultValue)})` : ''
      }

       if(field.type.toLowerCase().match(/(relationship)/)) {

        // set default 0 if relationship
        default_=''
        //unique_=''
        type_ = `${field.relationTo}`

        var selfOrAlreadyHasRelation = field.self ? `"${field.selfName}",`:''
        var selfAction =field.self ? ',onDelete: NoAction, onUpdate: NoAction' :''
       // field.name = !field.self ? field.name :field.selfName

    /// . 1 one-to-one 
      if(field.relationType=='one-to-one' && field.onSideRelation){
        // Nothing to do
        relationShip_=field.self ? `@relation("${field.selfName}")`:''
     }

     if(field.relationType=='one-to-one' && field?.onSideRelation){
      let localRelation = `${field.name}Id`
      relationShip_=`@relation(${selfOrAlreadyHasRelation}fields:[${localRelation}],references:[id]${selfAction})`
      relationShip_+=`
       ${localRelation} String${required_} @db.ObjectId @unique
      `
   }



        /// 2 - one-to-many 
      if(field.relationType.match(/(one-to-many)/) && field?.onSideRelation){ // 1er cas
       
            // Nothing todo 
            relationShip_=field.self ? `@relation("${field.selfName}")`:''

      }
  
             /// 3 many-to-one
      if(field.relationType.match(/(many-to-one)/)){ // 1er cas
              let localRelation = hasMany_ ? `${field.name}Ids`:`${field.name}Id`
               relationShip_=`@relation(${selfOrAlreadyHasRelation}fields:[${localRelation}],references:[id]${selfAction})`
               relationShip_+=`
             ${localRelation} String${required_} @db.ObjectId 
              `
      }

      // 4- many-many
      if(field.relationType.match(/(many-to-many)/)){ 
        let localRelation = hasMany_ ? `${field.name}Ids`:`${field.name}Id`
      
         relationShip_=`@relation(${selfOrAlreadyHasRelation}fields:[${localRelation}],references:[id]${selfAction})`
         relationShip_+=`
       ${localRelation} String${required_} @db.ObjectId 
         `
      }
    
      } // end of relationship
  
       // for composite type - mongodb only
       if(field.type.toLowerCase().match(/(composite)/)) {
        type_=`${field.composite}` 
       }
  
        // Condition si on a plusieurs données
      
      
  
       // Creation de chaque entrée field
      setField.value+=`
      ${field.name} ${type_}${required_}   ${default_} ${relationShip_} ${!relationShip_ ? unique_:''}`


    
      
    
    
    }

   
    // Creation des models
  
    if(schema.mode==='composite') schema.mode='type'

      

    // update ALl things
    await updateRole(schema,setField) 

    
    
   setSchema+=`
    ${schema.mode} ${schema.slug} {
      ${setField.value} ${schema.mode==="model"?timestamps:''}
      ${indexSet}
      ${
        schema.mode==="model" ?'@@index([createdAt])':''
      }
    }
    `
    
  }
 

  var PathSchema = cleanPath(process.cwd() + "/prisma/schema.prisma")
  fs.writeFileSync(PathSchema,setSchema,(err)=>{
  })

  

  


  } catch(err){
    console.error(err)
  }


    
    }






async function applyCollections(collections,config){

 try{
  var folderTmp =cleanPath(process.cwd()+'/.tmp/.collections')
  var pidFile =cleanPath(process.cwd()+'/.tmp/.pid')
  var dest =cleanPath(process.cwd()+'/collections/')
  
  //if(!fs.existsSync(folder)){
   // fs.mkdirSync(folder)
 // }

  var module_ = '@bitev.io/core/types'
  fsx.copySync(dest,folderTmp)
  fsx.emptyDirSync(dest)
  for await(var col of collections){

     
      let isRootCollection = rootCollections.find(e=>e.toLocaleLowerCase()==col.slug.toLowerCase())
      if(isRootCollection){
        col._v=true
        var filename = dest+`/_v.${col.slug}.js`
      } else {
        col._v=false
        var filename = dest+`/${col.slug}.js`
      }
      let fileCol = `
      const { Collection } = require("${module_}");\n
      module.exports= Collection(
        ${JSON.stringify(col)}
      )
      `
      fs.writeFileSync(filename,
        beautify(fileCol,{
          "indent_size": 4,
        })
        ,()=>{})
  }
 // await validationSchema()

  //await loadCollection(config)
 

  fs.writeFileSync(pidFile,'restart',()=>{})
//

 } catch(err){

  console.log(err)
 }




}

module.exports={
loadCollection,
applyCollections
}
