package controllers

import (
	"encoding/json"
	"fmt"
	"slices"

	oclib "cloud.o-forge.io/core/oc-lib"
	"cloud.o-forge.io/core/oc-lib/config"
	"cloud.o-forge.io/core/oc-lib/tools"
	beego "github.com/beego/beego/v2/server/web"
)

// Operations about workspace
type CollaborativeAreaController struct {
	beego.Controller
}

var paths = map[tools.DataType]map[tools.METHOD]string{ // paths used to call other OC services
	tools.COLLABORATIVE_AREA: {
		tools.DELETE: "/collaborative_area/:id?is_remote=true",
		tools.POST:   "/collaborative_area/?is_remote=true",
	},
	tools.WORKSPACE: {
		tools.DELETE: "/:id?is_remote=true",
		tools.POST:   "/?is_remote=true",
	},
	tools.WORKFLOW: {
		tools.DELETE: "/:id?is_remote=true",
		tools.POST:   "/?is_remote=true",
	},
	tools.PEER: {
		tools.POST: "/status/",
	},
}

// @Title Search
// @Description search shared workspace
// @Param	search		path 	string	true		"the word search you want to get"
// @Param 	is_draft        query   string  false       "draft wished"
// @Success 200 {shared workspace} models.shared_workspace
// @router /search/:search [get]
func (o *CollaborativeAreaController) Search() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	// store and return Id or post with UUID
	search := o.Ctx.Input.Param(":search")
	isDraft := o.Ctx.Input.Query("is_draft")
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).Search(nil, search, isDraft == "true")
	o.ServeJSON()
}

// @Title Update
// @Description create shared workspaces
// @Param	id		path 	string	true		"the shared workspace id you want to get"
// @Param	body		body 	models.workspace	true		"The shared workspace content"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id [put]
func (o *CollaborativeAreaController) Put() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	// store and return Id or post with UUID
	var res map[string]interface{}
	id := o.Ctx.Input.Param(":id")
	json.Unmarshal(o.Ctx.Input.CopyBody(10000), &res)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	fmt.Println("UPDATE", res)
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(res, id)
	o.ServeJSON()
}

// @Title Create
// @Description create shared workspace
// @Param	data		body 	json	true		"body for data content (Json format)"
// @Success 200 {shared workspace} models.shared_workspace
// @router / [post]
func (o *CollaborativeAreaController) Post() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	var res map[string]interface{}
	json.Unmarshal(o.Ctx.Input.CopyBody(10000), &res)
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).StoreOne(res)
	o.ServeJSON()
}

// @Title GetAll
// @Description find shared workspace by id
// @Param 	is_draft        query   string  false       "draft wished"
// @Success 200 {shared_workspace} models.shared_workspace
// @router / [get]
func (o *CollaborativeAreaController) GetAll() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	isDraft := o.Ctx.Input.Query("is_draft")
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadAll(isDraft == "true")
	o.ServeJSON()
}

// @Title Get
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id [get]
func (o *CollaborativeAreaController) Get() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	id := o.Ctx.Input.Param(":id")
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	o.ServeJSON()
}

// @Title Remove Workspace
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Param	id2		path 	string	true		"the id you want to add"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id/workspace/:id2 [delete]
func (o *CollaborativeAreaController) RemoveWorkspace() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	id := o.Ctx.Input.Param(":id")
	id2 := o.Ctx.Input.Param(":id2")
	r := oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	shared := r.ToCollaborativeArea()
	newWorkspace := []string{}
	if slices.Contains(shared.Workspaces, id2) {
		for _, w := range shared.Workspaces {
			if w != id2 {
				newWorkspace = append(newWorkspace, w)
			}
		}
		shared.Workspaces = newWorkspace
	}
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(
		shared.Serialize(shared), id)
	o.ServeJSON()
}

// @Title Remove Workflow
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Param	id2		path 	string	true		"the id you want to add"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id/workflow/:id2 [delete]
func (o *CollaborativeAreaController) RemoveWorkflow() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	id := o.Ctx.Input.Param(":id")
	id2 := o.Ctx.Input.Param(":id2")
	r := oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	shared := r.ToCollaborativeArea()
	newWorkflows := []string{}
	for _, w := range shared.Workflows {
		if w != id2 {
			newWorkflows = append(newWorkflows, w)
		}
	}
	shared.Workflows = newWorkflows
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(
		shared.Serialize(shared), id)
	o.ServeJSON()
}

// @Title Remove Peer
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Param	id2		path 	string	true		"the id you want to add"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id/peer/:id2 [delete]
func (o *CollaborativeAreaController) RemovePeer() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	id := o.Ctx.Input.Param(":id")
	id2 := o.Ctx.Input.Param(":id2")
	r := oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	shared := r.ToCollaborativeArea()
	newPeers := map[string][]string{}
	if shared.CreatorID == id2 {
		o.Data["json"] = map[string]interface{}{
			"data":  nil,
			"code":  409,
			"error": "You can't remove the creator from the shared workspace",
		}
		o.ServeJSON()
		return
	}
	if shared.AllowedPeersGroup != nil {
		keys := make([]string, len(shared.AllowedPeersGroup))
		i := 0
		for k := range shared.AllowedPeersGroup {
			keys[i] = k
			i++
		}
		if slices.Contains(keys, id) && shared.CreatorID != id2 {
			for _, peer := range keys {
				if peer != id2 {
					if config.GetConfig().Whitelist {
						newPeers[peer] = []string{"*"}
					} else {
						newPeers[peer] = []string{}
					}
				}
			}
			shared.AllowedPeersGroup = newPeers
		}
	}
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(
		shared.Serialize(shared), id)
	o.ServeJSON()
}

// @Title Remove Rule
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Param	id2		path 	string	true		"the id you want to add"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id/rule/:id2 [delete]
func (o *CollaborativeAreaController) RemoveRule() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	id := o.Ctx.Input.Param(":id")
	id2 := o.Ctx.Input.Param(":id2")
	r := oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	shared := r.ToCollaborativeArea()
	newRules := []string{}
	if shared != nil && slices.Contains(shared.Rules, id2) {
		for _, rule := range shared.Rules {
			if rule != id2 {
				newRules = append(newRules, rule)
			}
		}
		shared.Rules = newRules
	}
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(
		shared.Serialize(shared), id)
	o.ServeJSON()
}

// @Title Add Workspace
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Param	id2		path 	string	true		"the id you want to add"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id/workspace/:id2 [post]
func (o *CollaborativeAreaController) AddWorkspace() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	id := o.Ctx.Input.Param(":id")
	id2 := o.Ctx.Input.Param(":id2")
	r := oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	if r.Code != 200 {
		o.Data["json"] = r
		o.ServeJSON()
		return
	}
	shared := r.ToCollaborativeArea()
	if shared != nil && !slices.Contains(shared.Workspaces, id2) {
		shared.Workspaces = append(shared.Workspaces, id2)
	}
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(
		shared.Serialize(shared), id)
	o.ServeJSON()
}

// @Title Add Workflow
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Param	id2		path 	string	true		"the id you want to add"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id/workflow/:id2 [post]
func (o *CollaborativeAreaController) AddWorkflow() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	id := o.Ctx.Input.Param(":id")
	id2 := o.Ctx.Input.Param(":id2")
	r := oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	if r.Code != 200 {
		o.Data["json"] = r
		o.ServeJSON()
		return
	}
	shared := r.ToCollaborativeArea()
	if shared != nil && !slices.Contains(shared.Workflows, id2) {
		shared.Workflows = append(shared.Workflows, id2)
	}
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(
		shared.Serialize(shared), id)
	o.ServeJSON()
}

// @Title Add Peer
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Param	id2		path 	string	true		"the id you want to add"
// @Param	body		body 	models.workspace	true		"The shared workspace content"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id/peer/:id2 [post]
func (o *CollaborativeAreaController) AddPeer() {
	var res []string
	json.Unmarshal(o.Ctx.Input.CopyBody(10000), &res)
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	id := o.Ctx.Input.Param(":id")
	id2 := o.Ctx.Input.Param(":id2")
	r := oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	shared := r.ToCollaborativeArea()
	if shared.AllowedPeersGroup == nil {
		shared.AllowedPeersGroup = map[string][]string{}
	}
	if config.GetConfig().Whitelist && len(res) == 0 {
		shared.AllowedPeersGroup[id2] = []string{"*"}
	} else {
		shared.AllowedPeersGroup[id2] = res
	}
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(
		shared.Serialize(shared), id)
	o.ServeJSON()
}

// @Title Add Rule
// @Description find shared workspace by id
// @Param	id		path 	string	true		"the id you want to get"
// @Param	id2		path 	string	true		"the id you want to add"
// @Success 200 {shared workspace} models.shared_workspace
// @router /:id/rule/:id2 [post]
func (o *CollaborativeAreaController) AddRule() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	id := o.Ctx.Input.Param(":id")
	id2 := o.Ctx.Input.Param(":id2")
	r := oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, nil).LoadOne(id)
	shared := r.ToCollaborativeArea()
	if shared != nil && !slices.Contains(shared.Rules, id2) {
		shared.Rules = append(shared.Rules, id2)
	}
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).UpdateOne(shared.Serialize(shared), id)
	o.ServeJSON()
}

// @Title Delete
// @Description delete the shared workspace
// @Param	id		path 	string	true		"The id you want to delete"
// @Success 200 {shared workspace} delete success!
// @router /:id [delete]
func (o *CollaborativeAreaController) Delete() {
	user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request)
	id := o.Ctx.Input.Param(":id")
	caller := tools.NewHTTPCaller(paths) // caller used to send to peers
	caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true)
	o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.COLLABORATIVE_AREA), user, peerID, groups, caller).DeleteOne(id)
	o.ServeJSON()
}