package wayland

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

	"github.com/AvengeMedia/danklinux/internal/server/models"
)

type Request struct {
	ID     int                    `json:"id,omitempty"`
	Method string                 `json:"method"`
	Params map[string]interface{} `json:"params,omitempty"`
}

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

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

	switch req.Method {
	case "wayland.gamma.getState":
		handleGetState(conn, req, manager)
	case "wayland.gamma.setTemperature":
		handleSetTemperature(conn, req, manager)
	case "wayland.gamma.setLocation":
		handleSetLocation(conn, req, manager)
	case "wayland.gamma.setManualTimes":
		handleSetManualTimes(conn, req, manager)
	case "wayland.gamma.setUseIPLocation":
		handleSetUseIPLocation(conn, req, manager)
	case "wayland.gamma.setGamma":
		handleSetGamma(conn, req, manager)
	case "wayland.gamma.setEnabled":
		handleSetEnabled(conn, req, manager)
	case "wayland.gamma.subscribe":
		handleSubscribe(conn, req, manager)
	default:
		models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
	}
}

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

func handleSetTemperature(conn net.Conn, req Request, manager *Manager) {
	var lowTemp, highTemp int

	if temp, ok := req.Params["temp"].(float64); ok {
		lowTemp = int(temp)
		highTemp = int(temp)
	} else {
		low, okLow := req.Params["low"].(float64)
		high, okHigh := req.Params["high"].(float64)

		if !okLow || !okHigh {
			models.RespondError(conn, req.ID, "missing temperature parameters (provide 'temp' or both 'low' and 'high')")
			return
		}

		lowTemp = int(low)
		highTemp = int(high)
	}

	if err := manager.SetTemperature(lowTemp, highTemp); err != nil {
		models.RespondError(conn, req.ID, err.Error())
		return
	}

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

func handleSetLocation(conn net.Conn, req Request, manager *Manager) {
	lat, ok := req.Params["latitude"].(float64)
	if !ok {
		models.RespondError(conn, req.ID, "missing or invalid 'latitude' parameter")
		return
	}

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

	if err := manager.SetLocation(lat, lon); err != nil {
		models.RespondError(conn, req.ID, err.Error())
		return
	}

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

func handleSetManualTimes(conn net.Conn, req Request, manager *Manager) {
	sunriseParam := req.Params["sunrise"]
	sunsetParam := req.Params["sunset"]

	if sunriseParam == nil || sunsetParam == nil {
		manager.ClearManualTimes()
		models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "manual times cleared"})
		return
	}

	sunriseStr, ok := sunriseParam.(string)
	if !ok || sunriseStr == "" {
		manager.ClearManualTimes()
		models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "manual times cleared"})
		return
	}

	sunsetStr, ok := sunsetParam.(string)
	if !ok || sunsetStr == "" {
		manager.ClearManualTimes()
		models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "manual times cleared"})
		return
	}

	sunrise, err := time.Parse("15:04", sunriseStr)
	if err != nil {
		models.RespondError(conn, req.ID, "invalid sunrise format (use HH:MM)")
		return
	}

	sunset, err := time.Parse("15:04", sunsetStr)
	if err != nil {
		models.RespondError(conn, req.ID, "invalid sunset format (use HH:MM)")
		return
	}

	if err := manager.SetManualTimes(sunrise, sunset); err != nil {
		models.RespondError(conn, req.ID, err.Error())
		return
	}

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

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

	manager.SetUseIPLocation(use)
	models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "IP location preference set"})
}

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

	if err := manager.SetGamma(gamma); err != nil {
		models.RespondError(conn, req.ID, err.Error())
		return
	}

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

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

	manager.SetEnabled(enabled)
	models.Respond(conn, req.ID, SuccessResult{Success: true, Message: "enabled state set"})
}

func handleSubscribe(conn net.Conn, req 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
		}
	}
}
