package relay

import (
	"github.com/pion/webrtc/v4"
	"time"
)

// OnMessageCallback is a callback for messages of given type
type OnMessageCallback func(data []byte)

// MessageBase is the base type for WS/DC messages.
type MessageBase struct {
	PayloadType string          `json:"payload_type"`
	Latency     *LatencyTracker `json:"latency,omitempty"`
}

// MessageLog represents a log message.
type MessageLog struct {
	MessageBase
	Level   string `json:"level"`
	Message string `json:"message"`
	Time    string `json:"time"`
}

// MessageMetrics represents a metrics/heartbeat message.
type MessageMetrics struct {
	MessageBase
	UsageCPU        float64 `json:"usage_cpu"`
	UsageMemory     float64 `json:"usage_memory"`
	Uptime          uint64  `json:"uptime"`
	PipelineLatency float64 `json:"pipeline_latency"`
}

// MessageICECandidate represents an ICE candidate message.
type MessageICECandidate struct {
	MessageBase
	Candidate webrtc.ICECandidateInit `json:"candidate"`
}

// MessageSDP represents an SDP message.
type MessageSDP struct {
	MessageBase
	SDP webrtc.SessionDescription `json:"sdp"`
}

// JoinerType is an enum for the type of incoming room joiner
type JoinerType int

const (
	JoinerNode JoinerType = iota
	JoinerClient
)

func (jt *JoinerType) String() string {
	switch *jt {
	case JoinerNode:
		return "node"
	case JoinerClient:
		return "client"
	default:
		return "unknown"
	}
}

// MessageJoin is used to tell us that either participant or ingest wants to join the room
type MessageJoin struct {
	MessageBase
	JoinerType JoinerType `json:"joiner_type"`
}

// AnswerType is an enum for the type of answer, signaling Room state for a joiner
type AnswerType int

const (
	AnswerOffline AnswerType = iota // For participant/client, when the room is offline without stream
	AnswerInUse                     // For ingest/node joiner, when the room is already in use by another ingest/node
	AnswerOK                        // For both, when the join request is handled successfully
)

// MessageAnswer is used to send the answer to a join request
type MessageAnswer struct {
	MessageBase
	AnswerType AnswerType `json:"answer_type"`
}

// SendLogMessageWS sends a log message to the given WebSocket connection.
func (ws *SafeWebSocket) SendLogMessageWS(level, message string) error {
	msg := MessageLog{
		MessageBase: MessageBase{PayloadType: "log"},
		Level:       level,
		Message:     message,
		Time:        time.Now().Format(time.RFC3339),
	}
	return ws.SendJSON(msg)
}

// SendMetricsMessageWS sends a metrics message to the given WebSocket connection.
func (ws *SafeWebSocket) SendMetricsMessageWS(usageCPU, usageMemory float64, uptime uint64, pipelineLatency float64) error {
	msg := MessageMetrics{
		MessageBase:     MessageBase{PayloadType: "metrics"},
		UsageCPU:        usageCPU,
		UsageMemory:     usageMemory,
		Uptime:          uptime,
		PipelineLatency: pipelineLatency,
	}
	return ws.SendJSON(msg)
}

// SendICECandidateMessageWS sends an ICE candidate message to the given WebSocket connection.
func (ws *SafeWebSocket) SendICECandidateMessageWS(candidate webrtc.ICECandidateInit) error {
	msg := MessageICECandidate{
		MessageBase: MessageBase{PayloadType: "ice"},
		Candidate:   candidate,
	}
	return ws.SendJSON(msg)
}

// SendSDPMessageWS sends an SDP message to the given WebSocket connection.
func (ws *SafeWebSocket) SendSDPMessageWS(sdp webrtc.SessionDescription) error {
	msg := MessageSDP{
		MessageBase: MessageBase{PayloadType: "sdp"},
		SDP:         sdp,
	}
	return ws.SendJSON(msg)
}

// SendAnswerMessageWS sends an answer message to the given WebSocket connection.
func (ws *SafeWebSocket) SendAnswerMessageWS(answer AnswerType) error {
	msg := MessageAnswer{
		MessageBase: MessageBase{PayloadType: "answer"},
		AnswerType:  answer,
	}
	return ws.SendJSON(msg)
}
