package controllers

import (
	"encoding/json"
	"io"
	"net/http"
	"nodemaster/models"
	"time"

	beego "github.com/beego/beego/v2/server/web"
)

// NodesController manages the registry of remote nodes.
type NodesController struct {
	beego.Controller
}

// @Title GetAllNodes
// @Description list all registered remote nodes
// @Success 200 {object} []models.RemoteNode
// @router / [get]
func (c *NodesController) GetAll() {
	c.Data["json"] = models.GetAllRemoteNodes()
	c.ServeJSON()
}

// @Title AddNode
// @Description register a remote node
// @Param body body models.RemoteNode true "node to register"
// @Success 201 {object} models.RemoteNode
// @Failure 400 invalid body
// @router / [post]
func (c *NodesController) Post() {
	var n models.RemoteNode
	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &n); err != nil {
		c.Ctx.Output.SetStatus(400)
		c.Data["json"] = map[string]string{"error": err.Error()}
		c.ServeJSON()
		return
	}
	created, err := models.AddRemoteNode(n)
	if err != nil {
		c.Ctx.Output.SetStatus(500)
		c.Data["json"] = map[string]string{"error": err.Error()}
		c.ServeJSON()
		return
	}
	c.Ctx.Output.SetStatus(201)
	c.Data["json"] = created
	c.ServeJSON()
}

// @Title GetNode
// @Description get a remote node by id
// @Param id path string true "node id"
// @Success 200 {object} models.RemoteNode
// @Failure 404 not found
// @router /:id [get]
func (c *NodesController) Get() {
	id := c.Ctx.Input.Param(":id")
	n, err := models.GetRemoteNode(id)
	if err != nil {
		c.Ctx.Output.SetStatus(404)
		c.Data["json"] = map[string]string{"error": err.Error()}
		c.ServeJSON()
		return
	}
	c.Data["json"] = n
	c.ServeJSON()
}

// @Title UpdateNode
// @Description update a remote node
// @Param id path string true "node id"
// @Param body body models.RemoteNode true "updated node data"
// @Success 200 {object} models.RemoteNode
// @Failure 400 invalid body
// @Failure 404 not found
// @router /:id [put]
func (c *NodesController) Put() {
	id := c.Ctx.Input.Param(":id")
	var n models.RemoteNode
	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &n); err != nil {
		c.Ctx.Output.SetStatus(400)
		c.Data["json"] = map[string]string{"error": err.Error()}
		c.ServeJSON()
		return
	}
	updated, err := models.UpdateRemoteNode(id, n)
	if err != nil {
		c.Ctx.Output.SetStatus(404)
		c.Data["json"] = map[string]string{"error": err.Error()}
		c.ServeJSON()
		return
	}
	c.Data["json"] = updated
	c.ServeJSON()
}

// @Title DeleteNode
// @Description delete a remote node
// @Param id path string true "node id"
// @Success 200 {string} delete success
// @Failure 404 not found
// @router /:id [delete]
func (c *NodesController) Delete() {
	id := c.Ctx.Input.Param(":id")
	if err := models.DeleteRemoteNode(id); err != nil {
		c.Ctx.Output.SetStatus(404)
		c.Data["json"] = map[string]string{"error": err.Error()}
		c.ServeJSON()
		return
	}
	c.Data["json"] = map[string]string{"message": "delete success"}
	c.ServeJSON()
}

type nodeServices struct {
	Node     models.RemoteNode `json:"node"`
	Services []models.Service  `json:"services"`
	Error    string            `json:"error,omitempty"`
}

// @Title GetAggregated
// @Description query all registered nodes for their services and return an aggregated view
// @Success 200 {object} []nodeServices
// @router /aggregated [get]
func (c *NodesController) GetAggregated() {
	nodes := models.GetAllRemoteNodes()
	client := &http.Client{Timeout: 10 * time.Second}
	results := make([]nodeServices, 0, len(nodes))

	for _, node := range nodes {
		entry := nodeServices{Node: node}
		resp, err := client.Get(node.URL + "/v1/services")
		if err != nil {
			entry.Error = err.Error()
			results = append(results, entry)
			continue
		}
		body, err := io.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			entry.Error = err.Error()
			results = append(results, entry)
			continue
		}
		var svcs []models.Service
		if err := json.Unmarshal(body, &svcs); err != nil {
			entry.Error = "failed to parse services: " + err.Error()
		} else {
			entry.Services = svcs
		}
		results = append(results, entry)
	}

	c.Data["json"] = results
	c.ServeJSON()
}
