package main

import "encoding/json"

// Event is a Matrix ClientEvent as delivered in an appservice transaction. Each
// event carries its own room_id (unlike /sync, where it's implied by the room
// bucket). Content stays raw so each handler decodes only the shape it needs.
type Event struct {
	Type           string          `json:"type"`
	RoomID         string          `json:"room_id"`
	Sender         string          `json:"sender"`
	EventID        string          `json:"event_id"`
	OriginServerTS int64           `json:"origin_server_ts"`
	StateKey       *string         `json:"state_key,omitempty"`
	Content        json.RawMessage `json:"content"`
}

// Mentions is the MSC3952 intentional-mentions block. It can legitimately be the
// empty object `{}` (cinny writes it on every send), so UserIDs may be nil —
// callers must safe-deref (F29).
type Mentions struct {
	UserIDs []string `json:"user_ids"`
	Room    bool     `json:"room"`
}

type InReplyTo struct {
	EventID string `json:"event_id"`
}

type RelatesTo struct {
	RelType   string     `json:"rel_type"`
	EventID   string     `json:"event_id"`
	InReplyTo *InReplyTo `json:"m.in_reply_to"`
}

// MessageContent is the decoded m.room.message content we care about.
type MessageContent struct {
	MsgType       string          `json:"msgtype"`
	Body          string          `json:"body"`
	Format        string          `json:"format"`
	FormattedBody string          `json:"formatted_body"`
	Mentions      *Mentions       `json:"m.mentions"`
	RelatesTo     *RelatesTo      `json:"m.relates_to"`
	NewContent    json.RawMessage `json:"m.new_content"`
}

func (e *Event) DecodeMessage() (*MessageContent, bool) {
	if e.Type != "m.room.message" {
		return nil, false
	}
	var mc MessageContent
	if err := json.Unmarshal(e.Content, &mc); err != nil {
		return nil, false
	}
	return &mc, true
}

// IsReplace reports whether the message is an `m.replace` edit. Edits re-carry
// m.mentions and must NOT re-trigger or double-bill (F16).
func (mc *MessageContent) IsReplace() bool {
	return mc.RelatesTo != nil && mc.RelatesTo.RelType == "m.replace"
}

// membershipOf extracts the membership from an m.room.member event content.
func (e *Event) membershipOf() string {
	var m struct {
		Membership string `json:"membership"`
	}
	if json.Unmarshal(e.Content, &m) != nil {
		return ""
	}
	return m.Membership
}
