Source: utils/authChecks.js

import {query,UUID2hex,HEX2uuid} from '@commtool/sql-query'

import { Buffer } from 'node:buffer';
import { errorLoggerRead, errorLoggerUpdate } from './requestLogger.js';

export const checkAdmin=async (req,res,next, error)=>
{

    try {
        if(await isAdmin(req.session))
        {
            req.session.admin=true
            next()
            return
        }
        else
        {
            req.session.admin=false
            res.status(200).json({success:false,message:'your are not authorized for this operation'})
        }
    }
    catch(e)
    {
        errorLoggerUpdate(e)
        next()
    }


}

export const checkBaseAdmin=async (req,res,next)=>
{
    try {
        if(await isBaseAdmin(req.session))
        {
            req.session.baseAdmin=true
            next()
            return
        }
        else
        {
            req.session.baseAdmin=false
            res.status(401).json({success:false,message:'your base user is not authorized for this operation'})
        }
    }
    catch(e)
    {
        errorLoggerUpdate(e)
        return false
    }


}

/**
 * Check if user is admin for an organization
 * MIGRATION: Checks both Keycloak orgRoles (new) AND database admin list (legacy)
 * Once bot sync is complete, database check can be removed
 * 
 * @param {Buffer|string} user - User UID
 * @param {Buffer|string} orga - Organization UID (Buffer or hex string)
 * @param {Object} [session] - Optional session object to check Keycloak orgRoles
 * @returns {Promise<boolean|null>} true if admin, false if not admin, null if no admin list defined
 */
export const isAdminOrga = async (user, orga, session = null) => {
    try {
        // NEW: Check Keycloak organization roles first (faster, no DB query)
        // orgRoles is already filtered by sessionEnhancer to only include roles for current organization
        if (session?.roles) {
           return session.roles[HEX2uuid(orga).replace(/^UUID-/, '')].includes('db-admin');
        }
        return false;
    }
    catch(e) {
        errorLoggerUpdate(e);
        return false;
    }
}

/**
 * Check if user is admin for current organization
 * MIGRATION: Checks Keycloak orgRoles first, then falls back to database
 * 
 * @param {Object} session - Express session object
 * @returns {Promise<boolean>} true if user has admin rights
 */
export const isAdmin = async (session) => {
    try {
        // Bot user has admin rights
        
        if( session?.authUser?.groups.includes('app-bot') || session?.authUser?.groups.includes('employees') )
            return true;
        if(isEmployee(session))
        {
            session.admin= true;
            return true;
        }
        if(session.superadmin)
            return true;
        if(!session || !session.root || !session.authUser)
            return false;
    
        // Return cached result if available
        if(session && session.admin !== undefined && session.admin !== null)
            return session.admin;
        
        // NEW: Check Keycloak organization roles
        if(session.authUser?.orgRolesArray?.includes('db-admin')) {
            console.log('[isAdmin] ✅ User has org-admin role from Keycloak');
            session.admin = true;
            return true;
        }
        return session.admin;
    }
    catch(e) {
        errorLoggerUpdate(e);
        return false;
    }
}


/**
 * Check if the base user (real user, not impersonated) has admin rights
 * MIGRATION: Checks Keycloak orgRoles first, then falls back to database
 * 
 * @param {Object} session - Express session object
 * @returns {Promise<boolean>} true if base user has admin rights
 */
export const isBaseAdmin = async (session) => {
    try {
        if(!session || !session.root || !session.authUser )
            return false;
        if(session?.authUser?.groups.includes('app-bot') || session?.authUser?.groups.includes('employees'))
            return true;
        
        // Return cached result if available
        if(session && session.baseAdmin !== undefined)
            return session.baseAdmin;
        
        // NEW: Check Keycloak organization roles
        if(session.authUser?.orgRolesArray?.includes('db-admin')) {
            console.log('[isBaseAdmin] ✅ Base user has org-admin role from Keycloak');
            session.baseAdmin = true;
            return true;
        }

    }
    catch(e) {
        errorLoggerUpdate(e);
        return false;
    }
}



export const isObjectAdmin=async (req,check)=>
{
    try {
        // checks if the user can administrate an object
        if(!Buffer.isBuffer(check))
            check=UUID2hex(check)
        if(await isAdmin(req.session))
            return true
        if(!check)
            return false
        const result=await query(`SELECT Visible.UID FROM Visible
            WHERE Type IN('changeable','admin') AND UID=? AND UIDUser=?
                        `,[check,UUID2hex(req.session.user)])

        if(result.length===1)
            return true
        else
            return false
    }
    catch(e)
    {
        errorLoggerUpdate(e)
        return false
    }

}


export const checkObjectAdmin= async (req,res,next, error)=>
{
    try {
        if(await isAdmin(req.session))
        {
            next()
            return
        }
        // find all params which are UID's and check if we have changeable rights for them 
        const UIDs=Object.values(req.params)
            .filter(value=>(typeof value==='string' && value.match(/^UUID\-[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[1-5][0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$/) )) 
            .map(value=>UUID2hex(value))
        const results=  await query(`SELECT UID FROM Visible WHERE Type IN('changeable','admin') AND UID IN(?) AND UIDUser=?`,
                [UIDs,UUID2hex(req.session.user)])
        if(UIDs.length===0 || results.length===UIDs.length)
        {
            next()
            return
        }
        else
            res.status(200).json({success:false,message: 'user has no change authorisation for one this objects'})
    }
    catch(e)
    {
        errorLoggerUpdate(e)
        next()
    }

}

export const isListAdmin=async (req,check)=>
{
    try {
        // checks if the user can administrate a list
        
        check=UUID2hex(check)
        if(await isAdmin(req.session))
            return true
        const result=await query(`SELECT Visible.UID FROM Visible
            WHERE Type ='admin' AND UID=? AND UIDUser=?
                        `,[check,UUID2hex(req.session.user)])
    
        if(result.length===1)
            return true
        else
            return false
    }
    catch(e)
    {
        errorLoggerUpdate(e)
        return false
    }


}

export const isVisible=async (UID,user,root=null,session=null)=>
{

    try {
        if(user && user!=='undefined' && UID)
        {
            const visible= await query(`SELECT UID FROM Visible WHERE UID=? AND UIDUser=?`,[UID,user])
            return visible.length===1
        }
        if(root && session)
        {
            const admin=await isAdminOrga(UUID2hex(session.user),UUID2hex(session.root),session)
            return admin

        }
        return false
    }
    catch(e)
    {
        errorLoggerRead(e)
        return false
    }

}


export const isChangeable=async (UID,user,root=null,session=null)=>
{
    try {
        if(user && user!=='undefined' && UID)
        {
            const visible= await query(`SELECT UID FROM Visible WHERE UID=? AND UIDUser=? AND Type IN ('changeable','admin')`,[UID,user])
            return visible.length===1
        }
        if(root && session)
        {
            const admin=await isAdmin(session)
            return admin

        }
        return false
    }
    catch(e)
    {
        errorLoggerUpdate(e)
        return false
    }

   

}

export const isObjectVisible=async (req,check)=>
{
    try {
        // checks if the user can see an object
        if(!Buffer.isBuffer(check))
            check=UUID2hex(check)
        if(!check)
            return false
        if(await isAdmin(req.session))
            return true
        if(!check)
            return false
        const result=await query(`SELECT UID FROM Visible WHERE  UID=? AND UIDUser=?
                        `,[check,UUID2hex(req.session.user)])
        
        if(result.length===1)
            return true
        else
            return false
    }
    catch(e)
    {
        errorLoggerRead(e)
        return false
    }

}


export const checkVisible=async (req,res,next,error)=>
{
    try {
        if(await isAdmin(req.session) || !req.params)
        {
            next()
            return
        }

        // find all params which are UID's and check if we have visible rights for them 
        const UIDs=Object.values(req.params)
            .filter(value=>(typeof value==='string' && value.match(/^UUID\-[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[1-5][0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$/) )) 
            .map(value=>UUID2hex(value))
        if(UIDs.length===0)
        {
            res.status(404).send('no valid UUIDs supplied')
            return
        }
        const results=  await query(`SELECT UID FROM Visible WHERE UID IN(?) AND UIDUser=?`,
            [UIDs,UUID2hex(req.session.user)])
        if(UIDs.length===0 || results.length===UIDs.length)
        {
            next()
            return
        }
        else
        {
            res.status(200).json({success:false, message:'user not allowed to view one of the UUIDs'})
        }

    }
    catch(e)
    {
        errorLoggerRead(e)
        next()
    }
    
}

export const isEmployee = (session) => {
    try {
        

        if(!session  || !session.authUser)
        {
            console.log('no session, root or authUser')
            return false
        }
        if(session.authUser?.isBotAuth)
            return true

        
    
            
        // Check Keycloak groups for employee access
        if(session.authUser?.groups?.includes('employees') || 
           session.authUser?.groups?.includes('administrators'))
            return true
            
       
        return false
    }
    catch(e) {
        errorLoggerUpdate(e)
        return false
    }
}

export const checkEmployee = async (req, res, next) => {
    try {
        if(await isEmployee(req.session)) {
            req.session.employee = true
            next()
            return
        }
        else {
            req.session.employee = false
            res.status(200).json({success: false, message: 'Employee rights required for this operation'})
        }
    }
    catch(e) {
        errorLoggerUpdate(e)
        next()
    }
}