package dwl

import (
	"encoding/json"
	"fmt"
	"net"

	"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)

type SuccessResult struct {
	Success bool   `json:"success"`
	Message string `json:"message"`
}

func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
	if manager == nil {
		models.RespondError(conn, req.ID, "dwl manager not initialized")
		return
	}

	switch req.Method {
	case "dwl.getState":
		handleGetState(conn, req, manager)
	case "dwl.setTags":
		handleSetTags(conn, req, manager)
	case "dwl.setClientTags":
		handleSetClientTags(conn, req, manager)
	case "dwl.setLayout":
		handleSetLayout(conn, req, manager)
	case "dwl.subscribe":
		handleSubscribe(conn, req, manager)
	default:
		models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
	}
}

func handleGetState(conn net.Conn, req models.Request, manager *Manager) {
	state := manager.GetState()
	models.Respond(conn, req.ID, state)
}

func handleSetTags(conn net.Conn, req models.Request, manager *Manager) {
	output, ok := models.Get[string](req, "output")
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'output' parameter")
		return
	}

	tagmask, ok := models.Get[float64](req, "tagmask")
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'tagmask' parameter")
		return
	}

	toggleTagset, ok := models.Get[float64](req, "toggleTagset")
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'toggleTagset' parameter")
		return
	}

	if err := manager.SetTags(output, uint32(tagmask), uint32(toggleTagset)); err != nil {
		models.RespondError(conn, req.ID, err.Error())
		return
	}

	models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "tags set"})
}

func handleSetClientTags(conn net.Conn, req models.Request, manager *Manager) {
	output, ok := models.Get[string](req, "output")
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'output' parameter")
		return
	}

	andTags, ok := models.Get[float64](req, "andTags")
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'andTags' parameter")
		return
	}

	xorTags, ok := models.Get[float64](req, "xorTags")
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'xorTags' parameter")
		return
	}

	if err := manager.SetClientTags(output, uint32(andTags), uint32(xorTags)); err != nil {
		models.RespondError(conn, req.ID, err.Error())
		return
	}

	models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "client tags set"})
}

func handleSetLayout(conn net.Conn, req models.Request, manager *Manager) {
	output, ok := models.Get[string](req, "output")
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'output' parameter")
		return
	}

	index, ok := models.Get[float64](req, "index")
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'index' parameter")
		return
	}

	if err := manager.SetLayout(output, uint32(index)); err != nil {
		models.RespondError(conn, req.ID, err.Error())
		return
	}

	models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "layout set"})
}

func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) {
	clientID := fmt.Sprintf("client-%p", conn)
	stateChan := manager.Subscribe(clientID)
	defer manager.Unsubscribe(clientID)

	initialState := manager.GetState()
	if err := json.NewEncoder(conn).Encode(models.Response[State]{
		ID:     req.ID,
		Result: &initialState,
	}); err != nil {
		return
	}

	for state := range stateChan {
		if err := json.NewEncoder(conn).Encode(models.Response[State]{
			Result: &state,
		}); err != nil {
			return
		}
	}
}
