package extensions

import (
	"slices"
	"strconv"
	"time"

	"cwtch.im/cwtch/event"
	"cwtch.im/cwtch/model"
	"cwtch.im/cwtch/model/attr"
	"cwtch.im/cwtch/model/constants"
	"cwtch.im/cwtch/peer"
	"cwtch.im/cwtch/protocol/connections"
	"cwtch.im/cwtch/settings"
	"git.openprivacy.ca/openprivacy/log"
)

// SendWhenOnlineExtension implements automatic sending
// Some Considerations:
//   - There are race conditions inherant in this approach e.g. a peer could go offline just after recieving a message and never sending an ack
//   - In that case the next time we connect we will send a duplicate message.
//   - Currently we do not include metadata like sent time in raw peer protocols (however Overlay does now have support for that information)
type SendWhenOnlineExtension struct {
}

func (soe SendWhenOnlineExtension) NotifySettingsUpdate(_ settings.GlobalSettings) {
}

func (soe SendWhenOnlineExtension) EventsToRegister() []event.Type {
	return []event.Type{event.PeerStateChange}
}

func (soe SendWhenOnlineExtension) ExperimentsToRegister() []string {
	return nil
}

func (soe SendWhenOnlineExtension) OnEvent(ev event.Event, profile peer.CwtchPeer) {
	switch ev.EventType {
	case event.PeerStateChange:
		ci, err := profile.FetchConversationInfo(ev.Data["RemotePeer"])
		if err == nil {
			// if we have re-authenticated with thie peer then request their profile image...
			if connections.ConnectionStateToType()[ev.Data[event.ConnectionState]] == connections.AUTHENTICATED {
				log.Infof("Sending Offline Messages to %s", ci.Handle)
				// Check the last 100 messages, if any of them are pending, then send them now...
				messsages, _ := profile.GetMostRecentMessages(ci.ID, constants.CHANNEL_CHAT, 0, uint(100))
				slices.Reverse(messsages)
				for _, message := range messsages {
					if message.Attr[constants.AttrAck] == constants.False {
						sent, timeparseerr := time.Parse(time.RFC3339, message.Attr[constants.AttrSentTimestamp])
						if timeparseerr != nil {
							continue
						}
						if time.Since(sent) > time.Hour*24*7 {
							continue
						}
						body := message.Body
						ev := event.NewEvent(event.SendMessageToPeer, map[event.Field]string{event.ConversationID: strconv.Itoa(ci.ID), event.RemotePeer: ci.Handle, event.Data: body})
						ev.EventID = message.Signature // we need this ensure that we correctly ack this in the db when it comes back
						// TODO: The EventBus is becoming very noisy...we may want to consider a one-way shortcut to Engine i.e. profile.Engine.SendMessageToPeer
						log.Infof("resending message that was sent when peer was offline")
						profile.PublishEvent(ev)
					}
				}
				if ci.HasChannel(constants.CHANNEL_MANAGER) {
					messsages, _ = profile.GetMostRecentMessages(ci.ID, constants.CHANNEL_MANAGER, 0, uint(100))
					slices.Reverse(messsages)
					for _, message := range messsages {
						if message.Attr[constants.AttrAck] == constants.False {
							body := message.Body
							ev := event.NewEvent(event.SendMessageToPeer, map[event.Field]string{event.ConversationID: strconv.Itoa(ci.ID), event.RemotePeer: ci.Handle, event.Data: body})
							ev.EventID = message.Signature // we need this ensure that we correctly ack this in the db when it comes back
							// TODO: The EventBus is becoming very noisy...we may want to consider a one-way shortcut to Engine i.e. profile.Engine.SendMessageToPeer
							log.Debugf("resending message that was sent when peer was offline")
							profile.PublishEvent(ev)
						}
					}
				}
			}
		}
	}
}

// OnContactReceiveValue is nop for SendWhenOnnlineExtension
func (soe SendWhenOnlineExtension) OnContactReceiveValue(profile peer.CwtchPeer, conversation model.Conversation, szp attr.ScopedZonedPath, value string, exists bool) {
}

// OnContactRequestValue is nop for SendWhenOnnlineExtension
func (soe SendWhenOnlineExtension) OnContactRequestValue(profile peer.CwtchPeer, conversation model.Conversation, eventID string, szp attr.ScopedZonedPath) {

}
