package resource_model

import (
	"encoding/json"
	"slices"

	"cloud.o-forge.io/core/oc-lib/config"
	"cloud.o-forge.io/core/oc-lib/models/peer"
	"cloud.o-forge.io/core/oc-lib/models/utils"
	"cloud.o-forge.io/core/oc-lib/tools"
	"github.com/google/uuid"
)

type WebResource struct {
	Protocol string `bson:"protocol,omitempty" json:"protocol,omitempty"` // Protocol is the protocol of the URL
	Path     string `bson:"path,omitempty" json:"path,omitempty"`         // Path is the path of the URL
}

/*
* AbstractResource is a struct that represents a resource
* it defines the resource data
 */
type AbstractResource struct {
	utils.AbstractObject                // AbstractObject contains the basic fields of an object (id, name)
	ShortDescription     string         `json:"short_description,omitempty" bson:"short_description,omitempty" validate:"required"` // ShortDescription is the short description of the resource
	Description          string         `json:"description,omitempty" bson:"description,omitempty"`                                 // Description is the description of the resource
	Logo                 string         `json:"logo,omitempty" bson:"logo,omitempty" validate:"required"`                           // Logo is the logo of the resource
	Owner                string         `json:"owner,omitempty" bson:"owner,omitempty" validate:"required"`                         // Owner is the owner of the resource
	OwnerLogo            string         `json:"owner_logo,omitempty" bson:"owner_logo,omitempty"`                                   // OwnerLogo is the owner logo of the resource
	SourceUrl            string         `json:"source_url,omitempty" bson:"source_url,omitempty" validate:"required"`               // SourceUrl is the source URL of the resource
	PeerID               string         `json:"peer_id,omitempty" bson:"peer_id,omitempty" validate:"required"`                     // PeerID is the ID of the peer getting this resource
	License              string         `json:"license,omitempty" bson:"license,omitempty"`                                         // License is the license of the resource
	ResourceModel        *ResourceModel `json:"resource_model,omitempty" bson:"resource_model,omitempty"`                           // ResourceModel is the model of the resource

	AllowedPeersGroup map[string][]string `json:"allowed_peers_group,omitempty" bson:"allowed_peers_group,omitempty"` // AllowedPeersGroup is the group of allowed peers

	Price    string `json:"price,omitempty" bson:"price,omitempty"`       // Price is the price of access to the resource
	Currency string `json:"currency,omitempty" bson:"currency,omitempty"` // Currency is the currency of the price
}

func (ao *AbstractResource) GetAccessor(peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
	return nil
}

func (abs *AbstractResource) SetResourceModel(model *ResourceModel) {
	abs.ResourceModel = model
}

func (abs *AbstractResource) VerifyAuth(peerID string, groups []string) bool {
	if grps, ok := abs.AllowedPeersGroup[peerID]; ok || config.GetConfig().Whitelist {
		if (ok && slices.Contains(grps, "*")) || (!ok && config.GetConfig().Whitelist) {
			return true
		}
		for _, grp := range grps {
			if slices.Contains(groups, grp) {
				return true
			}
		}
	}
	return false
}

/*
* GetModelType returns the type of the model key
 */
func (abs *AbstractResource) GetModelType(cat string, key string) interface{} {
	if abs.ResourceModel == nil || abs.ResourceModel.Model == nil {
		return nil
	}
	if _, ok := abs.ResourceModel.Model[key]; !ok {
		return nil
	}
	return abs.ResourceModel.Model[cat][key].Type
}

/*
* GetModelKeys returns the keys of the model
 */
func (abs *AbstractResource) GetModelKeys() []string {
	keys := make([]string, 0)
	for k := range abs.ResourceModel.Model {
		keys = append(keys, k)
	}
	return keys
}

/*
* GetModelReadOnly returns the readonly of the model key
 */
func (abs *AbstractResource) GetModelReadOnly(cat string, key string) interface{} {
	if abs.ResourceModel == nil || abs.ResourceModel.Model == nil {
		return nil
	}
	if _, ok := abs.ResourceModel.Model[key]; !ok {
		return nil
	}
	return abs.ResourceModel.Model[cat][key].ReadOnly
}

func (d *AbstractResource) Trim() *AbstractResource {
	if ok, _ := (&peer.Peer{AbstractObject: utils.AbstractObject{UUID: d.PeerID}}).IsMySelf(); !ok {
		d.AllowedPeersGroup = map[string][]string{}
	}
	return d
}

type Model struct {
	Type     string `json:"type,omitempty" bson:"type,omitempty"`         // Type is the type of the model
	ReadOnly bool   `json:"readonly,omitempty" bson:"readonly,omitempty"` // ReadOnly is the readonly of the model
}

/*
* ResourceModel is a struct that represents a resource model
* it defines the resource metadata and specificity
* Warning: This struct is not user available, it is only used by the system
 */
type ResourceModel struct {
	UUID         string                      `json:"id,omitempty" bson:"id,omitempty" validate:"required"`
	ResourceType string                      `json:"resource_type,omitempty" bson:"resource_type,omitempty" validate:"required"`
	VarRefs      map[string]string           `json:"var_refs,omitempty" bson:"var_refs,omitempty"` // VarRefs is the variable references of the model
	Model        map[string]map[string]Model `json:"model,omitempty" bson:"model,omitempty"`
}

func (ao *ResourceModel) GetID() string {
	return ao.UUID
}

func (ao *ResourceModel) UpToDate() {}

func (r *ResourceModel) GenerateID() {
	r.UUID = uuid.New().String()
}

func (d *ResourceModel) GetName() string {
	return d.UUID
}

func (abs *ResourceModel) VerifyAuth(peerID string, groups []string) bool {
	return true
}

func (d *ResourceModel) GetAccessor(peerID string, groups []string, caller *tools.HTTPCaller) utils.Accessor {
	return &ResourceModelMongoAccessor{
		utils.AbstractAccessor{
			Type:   tools.RESOURCE_MODEL,
			PeerID: peerID,
			Groups: groups,
			Caller: caller,
		},
	}
}

func (dma *ResourceModel) Deserialize(j map[string]interface{}, obj utils.DBObject) utils.DBObject {
	b, err := json.Marshal(j)
	if err != nil {
		return nil
	}
	json.Unmarshal(b, obj)
	return obj
}

func (dma *ResourceModel) Serialize(obj utils.DBObject) map[string]interface{} {
	var m map[string]interface{}
	b, err := json.Marshal(obj)
	if err != nil {
		return nil
	}
	json.Unmarshal(b, &m)
	return m
}
