package oclib

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os"
	"slices"
	"strings"

	"runtime/debug"

	"cloud.o-forge.io/core/oc-lib/config"
	"cloud.o-forge.io/core/oc-lib/dbs"
	"cloud.o-forge.io/core/oc-lib/dbs/mongo"
	"cloud.o-forge.io/core/oc-lib/logs"
	"cloud.o-forge.io/core/oc-lib/models"
	"cloud.o-forge.io/core/oc-lib/models/billing"
	"cloud.o-forge.io/core/oc-lib/models/billing/discount"
	"cloud.o-forge.io/core/oc-lib/models/billing/payment"
	"cloud.o-forge.io/core/oc-lib/models/billing/refund"
	"cloud.o-forge.io/core/oc-lib/models/billing/subscription"
	"cloud.o-forge.io/core/oc-lib/models/booking"
	"cloud.o-forge.io/core/oc-lib/models/collaborative_area"
	"cloud.o-forge.io/core/oc-lib/models/collaborative_area/rules/rule"
	"cloud.o-forge.io/core/oc-lib/models/live"
	"cloud.o-forge.io/core/oc-lib/models/order"
	"cloud.o-forge.io/core/oc-lib/models/peer"
	"cloud.o-forge.io/core/oc-lib/models/resources"
	"cloud.o-forge.io/core/oc-lib/models/resources/purchase_resource"
	"cloud.o-forge.io/core/oc-lib/models/utils"
	w2 "cloud.o-forge.io/core/oc-lib/models/workflow"
	"cloud.o-forge.io/core/oc-lib/models/workflow_execution"
	"cloud.o-forge.io/core/oc-lib/models/workspace"
	"cloud.o-forge.io/core/oc-lib/tools"
	beego "github.com/beego/beego/v2/server/web"
	"github.com/beego/beego/v2/server/web/context"
	"github.com/beego/beego/v2/server/web/filter/cors"
	"github.com/google/uuid"
	"github.com/goraz/onion"
	"github.com/libp2p/go-libp2p/core/crypto"
	"github.com/rs/zerolog"
)

type Filters = dbs.Filters

type LibDataEnum int

// init accessible constant to retrieve data from the database
const (
	INVALID                LibDataEnum = iota
	DATA_RESOURCE                      = tools.DATA_RESOURCE
	PROCESSING_RESOURCE                = tools.PROCESSING_RESOURCE
	STORAGE_RESOURCE                   = tools.STORAGE_RESOURCE
	COMPUTE_RESOURCE                   = tools.COMPUTE_RESOURCE
	WORKFLOW_RESOURCE                  = tools.WORKFLOW_RESOURCE
	WORKFLOW                           = tools.WORKFLOW
	WORKSPACE                          = tools.WORKSPACE
	WORKFLOW_EXECUTION                 = tools.WORKFLOW_EXECUTION
	PEER                               = tools.PEER
	COLLABORATIVE_AREA                 = tools.COLLABORATIVE_AREA
	RULE                               = tools.RULE
	BOOKING                            = tools.BOOKING
	ORDER                              = tools.ORDER
	LIVE_DATACENTER                    = tools.LIVE_DATACENTER
	LIVE_STORAGE                       = tools.LIVE_STORAGE
	PURCHASE_RESOURCE                  = tools.PURCHASE_RESOURCE
	NATIVE_TOOL                        = tools.NATIVE_TOOL
	EXECUTION_VERIFICATION             = tools.EXECUTION_VERIFICATION
	ALLOWED_IMAGE                      = tools.ALLOWED_IMAGE
	SERVICE_RESOURCE                   = tools.SERVICE_RESOURCE
	LIVE_SERVICE                       = tools.LIVE_SERVICE
	BILL                               = tools.BILL
	PAYMENT                            = tools.PAYMENT
	REFUND                             = tools.REFUND
	DISCOUNT                           = tools.DISCOUNT
	SUBSCRIPTION                       = tools.SUBSCRIPTION
)

func FiltersFromFlatMap(flatMap map[string]interface{}, target interface{}) *dbs.Filters {
	return dbs.FiltersFromFlatMap(flatMap, target)
}

func GetMySelf() (*peer.Peer, error) {
	pp, err := utils.GetMySelf((&peer.Peer{}).GetAccessor(&tools.APIRequest{Admin: true}))
	if pp == nil {
		return nil, errors.New("peer not found")
	}
	return pp.(*peer.Peer), err
}

func IsMySelf(peerID string) (bool, string) {
	return utils.IsMySelf(peerID, (&peer.Peer{}).GetAccessor(&tools.APIRequest{Admin: true}))
}

func GenerateNodeID() (string, error) {
	folderStatic := "/var/lib/opencloud-node"
	if _, err := os.Stat(folderStatic); err == nil {
		os.MkdirAll(folderStatic, 0644)
	}
	folderStatic += "/node_id"
	if _, err := os.Stat(folderStatic); os.IsNotExist(err) {
		hostname, err := os.Hostname()
		if err != nil {
			return "", err
		}
		id := uuid.NewSHA1(uuid.NameSpaceOID, []byte("oc-"+hostname))
		err = os.WriteFile(folderStatic, []byte(id.String()), 0644)
		return id.String(), err
	}
	data, err := os.ReadFile(folderStatic)
	return string(data), err
}

// will turn into standards api hostnames
func (d LibDataEnum) API() string {
	return tools.Str[d]
}

// will turn into standards name
func (d LibDataEnum) String() string {
	return tools.Str[d]
}

// will turn into enum index
func (d LibDataEnum) EnumIndex() int {
	return int(d)
}

func IsQueryParamsEquals(input *context.BeegoInput, name string, val interface{}) bool {
	path := strings.Split(input.URI(), "?")
	if len(path) >= 2 {
		uri := strings.Split(path[1], "&")
		for _, val := range uri {
			kv := strings.Split(val, "=")
			if kv[0] == name && fmt.Sprintf("%v", val) == kv[1] {
				return true
			}
		}
	}
	return false
}

// model to define the shallow data structure
type LibDataShallow struct {
	Data []utils.ShallowDBObject `bson:"data" json:"data"`
	Code int                     `bson:"code" json:"code"`
	Err  string                  `bson:"error" json:"error"`
}

// model to define the data structure
type LibData struct {
	Data utils.DBObject `bson:"data" json:"data"`
	Code int            `bson:"code" json:"code"`
	Err  string         `bson:"error" json:"error"`
}

func InitDaemon(appName string) {
	beego.BConfig.AppName = appName
	config.SetAppName(appName) // set the app name to the logger to define the main log chan
	// create a temporary console logger for init
	logs.SetLogger(logs.CreateLogger("main"))
	// Load the right config file
	o := GetConfLoader(appName)
	// resources.InitNative()
	// feed the library with the loaded config
	SetConfig(
		o.GetBoolDefault("IS_NANO", false),
		o.GetBoolDefault("IS_API", true),

		o.GetStringDefault("MONGO_URL", "mongodb://127.0.0.1:27017"),
		o.GetStringDefault("MONGO_DATABASE", "DC_myDC"),
		o.GetStringDefault("NATS_URL", "nats://localhost:4222"),
		o.GetStringDefault("LOKI_URL", ""),
		o.GetStringDefault("LOG_LEVEL", "info"),
		o.GetIntDefault("API_PORT", 8080),
		o.GetStringDefault("PUBLIC_KEY_PATH", "./pem/public.pem"),
		o.GetStringDefault("PRIVATE_KEY_PATH", "./pem/private.pem"),

		o.GetStringDefault("INTERNAL_CATALOG_API", "oc-catalog"),
		o.GetStringDefault("INTERNAL_SHARED_API", "oc-shared"),
		o.GetStringDefault("INTERNAL_WORKFLOW_API", "oc-workflow"),
		o.GetStringDefault("INTERNAL_WORKSPACE_API", "oc-workspace"),
		o.GetStringDefault("INTERNAL_PEER_API", "oc-peer"),
		o.GetStringDefault("INTERNAL_DATACENTER_API", "oc-datacenter"),
		o.GetStringDefault("INTERNAL_SCHEDULER_API", "oc-scheduler"),
	)
	if config.GetConfig().IsApi {
		// Beego init
		beego.BConfig.AppName = appName
		beego.BConfig.Listen.HTTPPort = o.GetIntDefault("port", 8080)
		beego.BConfig.WebConfig.DirectoryIndex = true
		beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
	}
}

type IDTokenClaims struct {
	UserID string   `json:"user_id"`
	PeerID string   `json:"peer_id"`
	Groups []string `json:"groups"`
}

// SessionClaims struct
type SessionClaims struct {
	AccessToken map[string]interface{} `json:"access_token"`
	IDToken     IDTokenClaims          `json:"id_token"`
}

// Claims struct
type Claims struct {
	Session SessionClaims `json:"session"`
}

func GetExtends(objs []utils.ShallowDBObject, typ ...string) []map[string]interface{} {
	cache := map[tools.DataType]map[string]interface{}{}
	m := []map[string]interface{}{}
	for _, obj := range objs {
		m = append(m, GetExtend(obj, obj.Extend(typ...), cache))
	}
	return m
}

func GetExtend(obj utils.DBObject, extends map[string][]tools.DataType, cache map[tools.DataType]map[string]interface{}) map[string]interface{} {
	base := obj.Serialize(obj)
	for k, v := range extends {
		if base[k+"_id"] == nil || base[k+"_id"] == "" {
			fmt.Println(k+"_id", "GET EXTEND")
			continue
		}
		for _, vv := range v {
			if cache[vv] != nil && cache[vv][fmt.Sprintf("%v", base[k+"_id"])] != nil {
				base[k] = cache[vv][fmt.Sprintf("%v", base[k+"_id"])]
				continue
			}
			if d, _, err := models.Model(vv.EnumIndex()).GetAccessor(&tools.APIRequest{
				Admin: true,
			}).LoadOne(fmt.Sprintf("%v", base[k+"_id"])); d != nil && err == nil {
				base[k] = d.Serialize(d)
				if cache[vv] == nil {
					cache[vv] = map[string]interface{}{}
				}
				if cache[vv][fmt.Sprintf("%v", base[k+"_id"])] == nil {
					fmt.Println("TTT", vv, k, base[k])
					cache[vv][fmt.Sprintf("%v", base[k+"_id"])] = base[k]
				}
				break
			}
		}
	}
	return base
}

func ExtractTokenInfoWs(request http.Request) (string, string, []string) {
	reqToken := request.Header.Get("Sec-WebSocket-Protocol")
	if reqToken != "" {
		return extractFromToken(reqToken, "user_id"), extractFromToken(reqToken, "peer_id"), strings.Split(extractFromToken(reqToken, "groups"), ",")
	}
	return "", "", []string{}
}

func ExtractTokenInfo(request http.Request) (string, string, []string) {
	reqToken := request.Header.Get("Authorization")
	splitToken := strings.Split(reqToken, "Bearer ")
	if len(splitToken) < 2 {
		reqToken = ""
	} else {
		reqToken = splitToken[1]
	}
	if reqToken != "" {
		return extractFromToken(reqToken, "user_id"), extractFromToken(reqToken, "peer_id"), strings.Split(extractFromToken(reqToken, "groups"), ",")
	}
	return "", "", []string{}
}

func extractFromToken(token string, attr string) string {
	parts := strings.Split(token, ".")
	if len(parts) < 2 {
		return ""
	}
	payload := parts[1]
	switch len(payload) % 4 {
	case 2:
		payload += "=="
	case 3:
		payload += "="
	}
	b, err := base64.URLEncoding.DecodeString(payload)
	if err != nil {
		return ""
	}
	var claims map[string]interface{}
	if err := json.Unmarshal(b, &claims); err != nil {
		return ""
	}
	ext, ok := claims["ext"].(map[string]interface{})
	if !ok {
		return ""
	}
	peerID, _ := ext[attr].(string)
	return peerID
}

func InitAPI(appName string, extraRoutes ...map[string][]string) {
	InitDaemon(appName)
	if config.GetConfig().IsApi {
		beego.BConfig.Listen.HTTPPort = config.GetConfig().APIPort
		beego.BConfig.WebConfig.DirectoryIndex = true
		beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
		c := cors.Allow(&cors.Options{
			AllowAllOrigins:  true,
			AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
			AllowHeaders:     []string{"Origin", "Authorization", "Content-Type"},
			ExposeHeaders:    []string{"Content-Length", "Content-Type"},
			AllowCredentials: true,
		})
		beego.InsertFilter("*", beego.BeforeRouter, c)

		api := &tools.API{}
		api.Discovered(beego.BeeApp.Handlers.GetAllControllerInfo(), extraRoutes...)
	}
}

//
// Expose subpackages
//

/* GetLogger returns the main logger
* @return zerolog.Logger
 */
func GetLogger() zerolog.Logger {
	return logs.GetLogger()
}

/* SetConfig will set the config and create a logger according to app configuration and initialize mongo accessor
* @param url string
* @param database string
* @param natsUrl string
* @param lokiUrl string
* @param logLevel string
* @return *Config
 */
func SetConfig(isNano bool, isApi bool, mongoUrl string, database string, natsUrl string, lokiUrl string, logLevel string,
	port int, pppath string, pkpath string,
	internalCatalogAPI, internalSharedAPI, internalWorkflowAPI,
	internalWorkspaceAPI, internalPeerAPI, internalDatacenterAPI string, internalSchedulerAPI string) *config.Config {
	cfg := config.SetConfig(isNano, isApi, mongoUrl, database, natsUrl, lokiUrl, logLevel, port, pkpath, pppath, internalCatalogAPI, internalSharedAPI, internalWorkflowAPI,
		internalWorkspaceAPI, internalPeerAPI, internalDatacenterAPI, internalSchedulerAPI)
	defer func() {
		if r := recover(); r != nil {
			tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in Init : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack())))
			fmt.Printf("Panic recovered in Init : %v - %v\n", r, string(debug.Stack()))
		}
	}()
	logs.CreateLogger("main")
	mongo.MONGOService.Init(models.GetModelsNames(), config.GetConfig()) // init the mongo service
	return cfg
}

/* GetConfig will get the config
* @return *Config
 */
func GetConfig() *config.Config {
	return config.GetConfig()
}

/* GetConfLoader
* Get the configuration loader for the application
* Parameters:
* - AppName: string : the name of the application
* Returns:
* - *onion.Onion : the configuration loader
*  The configuration loader will load the configuration from the following sources:
*  - the environment variables with the prefix OCAPPNAME_
*  - the file /etc/oc/appname.json
*  - the file ./appname.json
*  The configuration loader will merge the configuration from the different sources
*  The configuration loader will give priority to the environment variables
*  The configuration loader will give priority to the local file over the default file
 */

func GetConfLoader(appName string) *onion.Onion {
	return config.GetConfLoader(appName)
}

type Request struct {
	Collection LibDataEnum
	User       string
	PeerID     string
	Groups     []string
	Caller     *tools.HTTPCaller
	admin      bool
}

func NewRequest(collection LibDataEnum, user string, peerID string, groups []string, caller *tools.HTTPCaller) *Request {
	return &Request{Collection: collection, User: user, PeerID: peerID, Groups: groups, Caller: caller}
}

func NewRequestInfoAdmin(collection LibDataEnum, user string, groups []string, caller *tools.HTTPCaller) *Request {
	p, err := GetMySelf()
	peerID := ""
	if p != nil && err == nil {
		peerID = p.GetID()
	}
	return &Request{Collection: collection, User: user, PeerID: peerID, Groups: groups, Caller: caller, admin: true}
}

func NewRequestAdmin(collection LibDataEnum, caller *tools.HTTPCaller) *Request {
	return &Request{Collection: collection, Caller: caller, admin: true}
}

/*
func (r *Request) PaymentTunnel(o *order.Order, scheduler *workflow_execution.WorkflowSchedule) error {
	return o.Pay(scheduler, &tools.APIRequest{
		Caller:   r.caller,
		Username: r.user,
		PeerID:   r.peerID,
		Groups:   r.groups,
	})
	return nil
}
*/
/*
* Search will search for the data in the database
* @param filters *dbs.Filters
* @param word string
* @param collection LibDataEnum
* @param c ...*tools.HTTPCaller
* @return data LibDataShallow
 */
func (r *Request) Search(filters *dbs.Filters, word string, isDraft bool, offset int64, limit int64) (data LibDataShallow) {
	defer func() { // recover the panic
		if r := recover(); r != nil {
			tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in Search : "+fmt.Sprintf("%v", r)))
			data = LibDataShallow{Data: nil, Code: 500, Err: "Panic recovered in LoadAll : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())}
		}
	}()
	d, code, err := models.Model(r.Collection.EnumIndex()).GetAccessor(&tools.APIRequest{
		Caller:   r.Caller,
		Username: r.User,
		PeerID:   r.PeerID,
		Groups:   r.Groups,
		Admin:    r.admin,
	}).Search(filters, word, isDraft, offset, limit)
	if err != nil {
		data = LibDataShallow{Data: d, Code: code, Err: err.Error()}
		return
	}
	data = LibDataShallow{Data: d, Code: code}
	return
}

/*
* LoadAll will load all the data from the database
* @param collection LibDataEnum
* @param c ...*tools.HTTPCaller
* @return data LibDataShallow
 */
func (r *Request) LoadAll(isDraft bool, offset int64, limit int64) (data LibDataShallow) {
	defer func() { // recover the panic
		if r := recover(); r != nil {
			tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in LoadAll : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack())))
			data = LibDataShallow{Data: nil, Code: 500, Err: "Panic recovered in LoadAll : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())}
		}
	}()
	d, code, err := models.Model(r.Collection.EnumIndex()).GetAccessor(&tools.APIRequest{
		Caller:   r.Caller,
		Username: r.User,
		PeerID:   r.PeerID,
		Groups:   r.Groups,
		Admin:    r.admin,
	}).LoadAll(isDraft, offset, limit)
	if err != nil {
		data = LibDataShallow{Data: d, Code: code, Err: err.Error()}
		return
	}
	data = LibDataShallow{Data: d, Code: code}
	return
}

/*
* LoadOne will load one data from the database
* @param collection LibDataEnum
* @param id string
* @param c ...*tools.HTTPCaller
* @return data LibData
 */
func (r *Request) LoadOne(id string) (data LibData) {
	defer func() { // recover the panic
		if r := recover(); r != nil {
			tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in LoadOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack())))
			data = LibData{Data: nil, Code: 500, Err: "Panic recovered in LoadOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())}
		}
	}()
	d, code, err := models.Model(r.Collection.EnumIndex()).GetAccessor(&tools.APIRequest{
		Caller:   r.Caller,
		Username: r.User,
		PeerID:   r.PeerID,
		Groups:   r.Groups,
		Admin:    r.admin,
	}).LoadOne(id)
	if err != nil {
		data = LibData{Data: d, Code: code, Err: err.Error()}
		return
	}
	data = LibData{Data: d, Code: code}
	return
}

/*
* UpdateOne will update one data from the database
* @param collection LibDataEnum
* @param set map[string]interface{}
* @param id string
* @param c ...*tools.HTTPCaller
* @return data LibData
 */
func (r *Request) UpdateOne(set map[string]interface{}, id string) (data LibData) {
	defer func() { // recover the panic
		if r := recover(); r != nil {
			tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in UpdateOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack())))
			data = LibData{Data: nil, Code: 500, Err: "Panic recovered in UpdateOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())}
		}
	}()
	model := models.Model(r.Collection.EnumIndex())
	d, code, err := model.GetAccessor(&tools.APIRequest{
		Caller:   r.Caller,
		Username: r.User,
		PeerID:   r.PeerID,
		Groups:   r.Groups,
		Admin:    r.admin,
	}).UpdateOne(set, id)
	if err != nil {
		data = LibData{Data: d, Code: code, Err: err.Error()}
		return
	}
	data = LibData{Data: d, Code: code}
	return
}

/*
* DeleteOne will delete one data from the database
* @param collection LibDataEnum
* @param id string
* @param c ...*tools.HTTPCaller
* @return data LibData
 */
func (r *Request) DeleteOne(id string) (data LibData) {
	defer func() { // recover the panic
		if r := recover(); r != nil {
			tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in DeleteOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack())))
			data = LibData{Data: nil, Code: 500, Err: "Panic recovered in DeleteOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())}
		}
	}()
	d, code, err := models.Model(r.Collection.EnumIndex()).GetAccessor(&tools.APIRequest{
		Caller:   r.Caller,
		Username: r.User,
		PeerID:   r.PeerID,
		Groups:   r.Groups,
		Admin:    r.admin,
	}).DeleteOne(id)
	if err != nil {
		data = LibData{Data: d, Code: code, Err: err.Error()}
		return
	}
	data = LibData{Data: d, Code: code}
	return
}

/*
* StoreOne will store one data from the database
* @param collection LibDataEnum
* @param object map[string]interface{}
* @param c ...*tools.HTTPCaller
* @return data LibData
 */
func (r *Request) StoreOne(object map[string]interface{}) (data LibData) {
	defer func() { // recover the panic
		if r := recover(); r != nil {
			tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in StoreOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack())))
			data = LibData{Data: nil, Code: 500, Err: "Panic recovered in StoreOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())}
		}
	}()
	model := models.Model(r.Collection.EnumIndex())
	d, code, err := model.GetAccessor(&tools.APIRequest{
		Caller:   r.Caller,
		Username: r.User,
		PeerID:   r.PeerID,
		Groups:   r.Groups,
		Admin:    r.admin,
	}).StoreOne(model.Deserialize(object, model))
	if err != nil {
		data = LibData{Data: d, Code: code, Err: err.Error()}
		return
	}
	data = LibData{Data: d, Code: code}
	return
}

/*
* CopyOne will copy one data from the database
* @param collection LibDataEnum
* @param object map[string]interface{}
* @param c ...*tools.HTTPCaller
* @return data LibData
 */
func (r *Request) CopyOne(object map[string]interface{}) (data LibData) {
	defer func() { // recover the panic
		if r := recover(); r != nil {
			tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in CopyOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack())))
			data = LibData{Data: nil, Code: 500, Err: "Panic recovered in UpdateOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())}
		}
	}()
	model := models.Model(r.Collection.EnumIndex())
	d, code, err := model.GetAccessor(&tools.APIRequest{
		Caller:   r.Caller,
		Username: r.User,
		PeerID:   r.PeerID,
		Groups:   r.Groups,
		Admin:    r.admin,
	}).CopyOne(model.Deserialize(object, model))
	if err != nil {
		data = LibData{Data: d, Code: code, Err: err.Error()}
		return
	}
	data = LibData{Data: d, Code: code}
	return
}

// ================ CAST ========================= //

func (l *LibData) ToDataResource() *resources.DataResource {
	if l.Data.GetAccessor(nil).GetType() == tools.DATA_RESOURCE {
		return l.Data.(*resources.DataResource)
	}
	return nil
}

func (l *LibData) ToComputeResource() *resources.ComputeResource {
	if l.Data != nil && l.Data.GetAccessor(nil).GetType() == tools.COMPUTE_RESOURCE {
		return l.Data.(*resources.ComputeResource)
	}
	return nil
}
func (l *LibData) ToStorageResource() *resources.StorageResource {
	if l.Data.GetAccessor(nil).GetType() == tools.STORAGE_RESOURCE {
		return l.Data.(*resources.StorageResource)
	}
	return nil
}
func (l *LibData) ToProcessingResource() *resources.ProcessingResource {
	if l.Data.GetAccessor(nil).GetType() == tools.PROCESSING_RESOURCE {
		return l.Data.(*resources.ProcessingResource)
	}
	return nil
}
func (l *LibData) ToWorkflowResource() *resources.WorkflowResource {
	if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW_RESOURCE {
		return l.Data.(*resources.WorkflowResource)
	}
	return nil
}
func (l *LibData) ToPeer() *peer.Peer {
	if l.Data.GetAccessor(nil).GetType() == tools.PEER {
		return l.Data.(*peer.Peer)
	}
	return nil
}

func (l *LibData) ToWorkflow() *w2.Workflow {
	if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW {
		return l.Data.(*w2.Workflow)
	}
	return nil
}
func (l *LibData) ToWorkspace() *workspace.Workspace {
	if l.Data.GetAccessor(nil).GetType() == tools.WORKSPACE {
		return l.Data.(*workspace.Workspace)
	}
	return nil
}

func (l *LibData) ToCollaborativeArea() *collaborative_area.CollaborativeArea {
	if l.Data.GetAccessor(nil).GetType() == tools.COLLABORATIVE_AREA {
		return l.Data.(*collaborative_area.CollaborativeArea)
	}
	return nil
}

func (l *LibData) ToRule() *rule.Rule {
	if l.Data.GetAccessor(nil).GetType() == tools.COLLABORATIVE_AREA {
		return l.Data.(*rule.Rule)
	}
	return nil
}

func (l *LibData) ToWorkflowExecution() *workflow_execution.WorkflowExecution {
	if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW_EXECUTION {
		return l.Data.(*workflow_execution.WorkflowExecution)
	}
	return nil
}

func (l *LibData) ToOrder() *order.Order {
	if l.Data.GetAccessor(nil).GetType() == tools.ORDER {
		return l.Data.(*order.Order)
	}
	return nil
}

func (l *LibData) ToLiveDatacenter() *live.LiveDatacenter {
	if l.Data.GetAccessor(nil).GetType() == tools.LIVE_DATACENTER {
		return l.Data.(*live.LiveDatacenter)
	}
	return nil
}

func (l *LibData) ToLiveStorage() *live.LiveStorage {
	if l.Data.GetAccessor(nil).GetType() == tools.LIVE_STORAGE {
		return l.Data.(*live.LiveStorage)
	}
	return nil
}

func (l *LibData) ToBookings() *booking.Booking {
	if l.Data.GetAccessor(nil).GetType() == tools.BOOKING {
		return l.Data.(*booking.Booking)
	}
	return nil
}

func (l *LibData) ToPurchasedResource() *purchase_resource.PurchaseResource {
	if l.Data.GetAccessor(nil).GetType() == tools.PURCHASE_RESOURCE {
		return l.Data.(*purchase_resource.PurchaseResource)
	}
	return nil
}

func (l *LibData) ToBill() *billing.Bill {
	if l.Data != nil && l.Data.GetAccessor(nil).GetType() == tools.BILL {
		return l.Data.(*billing.Bill)
	}
	return nil
}

func (l *LibData) ToPayment() *payment.Payment {
	if l.Data != nil && l.Data.GetAccessor(nil).GetType() == tools.PAYMENT {
		return l.Data.(*payment.Payment)
	}
	return nil
}

func (l *LibData) ToRefund() *refund.Refund {
	if l.Data != nil && l.Data.GetAccessor(nil).GetType() == tools.REFUND {
		return l.Data.(*refund.Refund)
	}
	return nil
}

func (l *LibData) ToDiscount() *discount.Discount {
	if l.Data != nil && l.Data.GetAccessor(nil).GetType() == tools.DISCOUNT {
		return l.Data.(*discount.Discount)
	}
	return nil
}

func (l *LibData) ToSubscription() *subscription.Subscription {
	if l.Data != nil && l.Data.GetAccessor(nil).GetType() == tools.SUBSCRIPTION {
		return l.Data.(*subscription.Subscription)
	}
	return nil
}

// ------------- Loading resources ----------GetAccessor

func LoadOneStorage(storageId string, user string, peerID string, groups []string) (*resources.StorageResource, error) {

	res := NewRequest(LibDataEnum(STORAGE_RESOURCE), user, peerID, groups, nil).LoadOne(storageId)
	if res.Code != 200 {
		l := GetLogger()
		l.Error().Msg("Error while loading storage ressource " + storageId)
		return nil, errors.New(res.Err)
	}

	return res.ToStorageResource(), nil
}

func LoadOneComputing(computingId string, user string, peerID string, groups []string) (*resources.ComputeResource, error) {

	res := NewRequest(LibDataEnum(COMPUTE_RESOURCE), user, peerID, groups, nil).LoadOne(computingId)
	if res.Code != 200 {
		l := GetLogger()
		l.Error().Msg("Error while loading computing ressource " + computingId)
		return nil, errors.New(res.Err)
	}

	return res.ToComputeResource(), nil
}

func LoadOneProcessing(processingId string, user string, peerID string, groups []string) (*resources.ProcessingResource, error) {

	res := NewRequest(LibDataEnum(PROCESSING_RESOURCE), user, peerID, groups, nil).LoadOne(processingId)
	if res.Code != 200 {
		l := GetLogger()
		l.Error().Msg("Error while loading processing ressource " + processingId)
		return nil, errors.New(res.Err)
	}

	return res.ToProcessingResource(), nil
}

func LoadOneData(dataId string, user string, peerID string, groups []string) (*resources.DataResource, error) {

	res := NewRequest(LibDataEnum(DATA_RESOURCE), user, peerID, groups, nil).LoadOne(dataId)
	if res.Code != 200 {
		l := GetLogger()
		l.Error().Msg("Error while loading data ressource " + dataId)
		return nil, errors.New(res.Err)
	}
	return res.ToDataResource(), nil

}

// verify signature...
func InitNATSDecentralizedEmitter(authorizedDT ...tools.DataType) {
	tools.NewNATSCaller().ListenNats(map[tools.NATSMethod]func(tools.NATSResponse){
		tools.CREATE_RESOURCE: func(resp tools.NATSResponse) {
			if resp.FromApp == config.GetAppName() || !slices.Contains(authorizedDT, resp.Datatype) {
				return
			}
			p := map[string]interface{}{}
			if err := json.Unmarshal(resp.Payload, &p); err == nil {
				if err := verify(resp.Payload); err != nil {
					return // don't trust anyone... only friends and foes are privilege
				}
				access := NewRequestAdmin(LibDataEnum(resp.Datatype), nil)
				if data := access.Search(nil, fmt.Sprintf("%v", p[resp.SearchAttr]), false, 0, 1); len(data.Data) > 0 {
					delete(p, "id")
					access.UpdateOne(p, data.Data[0].GetID())
				} else {
					access.StoreOne(p)
				}
			}
		},
		tools.REMOVE_RESOURCE: func(resp tools.NATSResponse) {
			if resp.FromApp == config.GetAppName() || !slices.Contains(authorizedDT, resp.Datatype) {
				return
			}
			if err := verify(resp.Payload); err != nil {
				return // don't trust anyone... only friends and foes are privilege
			}
			p := map[string]interface{}{}
			access := NewRequestAdmin(LibDataEnum(resp.Datatype), nil)
			err := json.Unmarshal(resp.Payload, &p)
			if err == nil {
				if data := access.Search(nil, fmt.Sprintf("%v", p[resp.SearchAttr]), false, 0, 1); len(data.Data) > 0 {
					access.DeleteOne(data.Data[0].GetID())
				}
			}

		},
	})
}

func verify(payload []byte) error {
	var obj utils.AbstractObject
	if err := json.Unmarshal(payload, &obj); err == nil {
		obj.Unsign()
		origin := NewRequestAdmin(LibDataEnum(PEER), nil).LoadOne(obj.GetCreatorID())
		if origin.Data == nil || origin.Data.(*peer.Peer).Relation != peer.PARTNER {
			return errors.New("don't know personnaly this guy") // don't trust anyone... only friends and foes are privilege
		}
		data, err := base64.StdEncoding.DecodeString(origin.Data.(*peer.Peer).PublicKey)
		if err != nil {
			return err
		}
		pk, err := crypto.UnmarshalPublicKey(data)
		if err != nil {
			return err
		}
		b, err := json.Marshal(obj)
		if err != nil {
			return err
		}
		if ok, err := pk.Verify(b, obj.GetSignature()); err != nil {
			return err
		} else if !ok {
			return errors.New("signature is not corresponding to public key")
		} else {
			return nil
		}
	} else {
		return err
	}
}
