package entities

import (
	"time"

	"github.com/google/uuid"
	"github.com/rwadurian/mpc-system/services/session-coordinator/domain/value_objects"
)

// SessionMessage represents an MPC message (encrypted, Coordinator does not decrypt)
type SessionMessage struct {
	ID          uuid.UUID
	SessionID   value_objects.SessionID
	FromParty   value_objects.PartyID
	ToParties   []value_objects.PartyID // nil means broadcast
	RoundNumber int
	MessageType string
	Payload     []byte // Encrypted MPC protocol message
	CreatedAt   time.Time
	DeliveredAt *time.Time
}

// NewSessionMessage creates a new session message
func NewSessionMessage(
	sessionID value_objects.SessionID,
	fromParty value_objects.PartyID,
	toParties []value_objects.PartyID,
	roundNumber int,
	messageType string,
	payload []byte,
) *SessionMessage {
	return &SessionMessage{
		ID:          uuid.New(),
		SessionID:   sessionID,
		FromParty:   fromParty,
		ToParties:   toParties,
		RoundNumber: roundNumber,
		MessageType: messageType,
		Payload:     payload,
		CreatedAt:   time.Now().UTC(),
	}
}

// IsBroadcast checks if the message is a broadcast
func (m *SessionMessage) IsBroadcast() bool {
	return len(m.ToParties) == 0
}

// IsFor checks if the message is for a specific party
func (m *SessionMessage) IsFor(partyID value_objects.PartyID) bool {
	if m.IsBroadcast() {
		// Broadcast is for everyone except sender
		return !m.FromParty.Equals(partyID)
	}

	for _, to := range m.ToParties {
		if to.Equals(partyID) {
			return true
		}
	}
	return false
}

// MarkDelivered marks the message as delivered
func (m *SessionMessage) MarkDelivered() {
	now := time.Now().UTC()
	m.DeliveredAt = &now
}

// IsDelivered checks if the message has been delivered
func (m *SessionMessage) IsDelivered() bool {
	return m.DeliveredAt != nil
}

// GetToPartyStrings returns to parties as strings
func (m *SessionMessage) GetToPartyStrings() []string {
	if m.IsBroadcast() {
		return nil
	}
	result := make([]string, len(m.ToParties))
	for i, p := range m.ToParties {
		result[i] = p.String()
	}
	return result
}

// ToDTO converts to a DTO
func (m *SessionMessage) ToDTO() MessageDTO {
	toParties := m.GetToPartyStrings()
	return MessageDTO{
		ID:          m.ID.String(),
		SessionID:   m.SessionID.String(),
		FromParty:   m.FromParty.String(),
		ToParties:   toParties,
		IsBroadcast: m.IsBroadcast(),
		RoundNumber: m.RoundNumber,
		MessageType: m.MessageType,
		Payload:     m.Payload,
		CreatedAt:   m.CreatedAt,
	}
}

// MessageDTO is a data transfer object for messages
type MessageDTO struct {
	ID          string    `json:"id"`
	SessionID   string    `json:"session_id"`
	FromParty   string    `json:"from_party"`
	ToParties   []string  `json:"to_parties,omitempty"`
	IsBroadcast bool      `json:"is_broadcast"`
	RoundNumber int       `json:"round_number"`
	MessageType string    `json:"message_type"`
	Payload     []byte    `json:"payload"`
	CreatedAt   time.Time `json:"created_at"`
}
