////////////////////////////////////////////////////////////////////////////////
// Copyright © 2022 xx foundation                                             //
//                                                                            //
// Use of this source code is governed by a license that can be found in the  //
// LICENSE file                                                               //
////////////////////////////////////////////////////////////////////////////////

package channels

import (
	"crypto/ed25519"
	jww "github.com/spf13/jwalterweatherman"
	"gitlab.com/elixxir/client/cmix/identity/receptionID"
	"gitlab.com/elixxir/client/cmix/rounds"
	"gitlab.com/elixxir/primitives/states"
	"gitlab.com/xx_network/primitives/id"
	"time"
)

// the userListener adheres to the [broadcast.ListenerFunc] interface and is
// used when user messages are received on the channel
type userListener struct {
	name      NameService
	chID      *id.ID
	trigger   triggerEventFunc
	checkSent messageReceiveFunc
}

// Listen is called when a message is received for the user listener
func (ul *userListener) Listen(payload []byte,
	receptionID receptionID.EphemeralIdentity, round rounds.Round) {

	//Decode the message as a user message
	umi, err := unmarshalUserMessageInternal(payload, ul.chID)
	if err != nil {
		jww.WARN.Printf("Failed to unmarshal User Message on "+
			"channel %s", ul.chID)
		return
	}

	um := umi.GetUserMessage()
	cm := umi.GetChannelMessage()
	msgID := umi.GetMessageID()

	//check if we sent the message, ignore triggering if we sent
	if ul.checkSent(msgID, round) {
		return
	}

	/*CRYPTOGRAPHICALLY RELEVANT CHECKS*/

	// check the round to ensure the message is not a replay
	if id.Round(cm.RoundID) != round.ID {
		jww.WARN.Printf("The round message %s send on %d referenced "+
			"(%d) was not the same as the round the message was found on (%d)",
			msgID, ul.chID, cm.RoundID, round.ID)
		return
	}

	// check that the user properly signed the message
	if !ed25519.Verify(um.ECCPublicKey, um.Message, um.Signature) {
		jww.WARN.Printf("Message %s on channel %s purportedly from %s "+
			"failed its user signature with signature %v", msgID,
			ul.chID, cm.Nickname, um.Signature)
		return
	}

	// Replace the timestamp on the message if it is outside of the
	// allowable range
	ts := vetTimestamp(time.Unix(0, cm.LocalTimestamp), round.Timestamps[states.QUEUED], msgID)

	//TODO: Processing of the message relative to admin commands will be here

	//Submit the message to the event model for listening
	if uuid, err := ul.trigger(ul.chID, umi, ts, receptionID, round,
		Delivered); err != nil {
		jww.WARN.Printf("Error in passing off trigger for "+
			"message (UUID: %d): %+v", uuid, err)
	}

	return
}