package sd.model;

import java.util.UUID;

import sd.protocol.MessageProtocol;

/**
 * Envelope fundamental do protocolo de comunicação entre processos distribuídos (IPC).
 * <p>
 * Esta classe atua como a Unidade de Dados de Aplicação (ADU), encapsulando tanto
 * os metadados de roteamento (origem, destino, tipo) quanto a carga útil (payload)
 * polimórfica. É agnóstica ao conteúdo, servindo como contentor genérico para
 * transferência de estado (Veículos, Estatísticas) ou sinais de controlo (Semáforos).
 * <p>
 * A imutabilidade dos campos (exceto via serialização) garante a integridade da mensagem
 * durante o trânsito na rede.
 */
public class Message implements MessageProtocol {
    
    private static final long serialVersionUID = 1L;
    
    /** * Identificador único universal (UUID). 
     * Essencial para rastreabilidade (tracing), logs de auditoria e mecanismos de deduplicação.
     */
    private final String messageId;
    
    /** Discriminador semântico que define como o recetor deve processar o payload. */
    private final MessageType type;
    
    /** Identificador lógico do nó emissor (ex: "Cr1", "Coordinator"). */
    private final String senderId;
    
    /** * Identificador lógico do nó recetor. 
     * Se {@code null}, a mensagem deve ser tratada como <b>Broadcast</b>.
     */
    private final String destinationId;
    
    /** * Carga útil polimórfica. 
     * Deve implementar {@link java.io.Serializable} para garantir transmissão correta.
     */
    private final Object payload;
    
    /** Marca temporal da criação da mensagem (Unix Timestamp), usada para cálculo de latência de rede. */
    private final long timestamp;
    
    /**
     * Construtor completo para reconstrução de mensagens ou envio com timestamp manual.
     *
     * @param type Classificação semântica da mensagem.
     * @param senderId ID do processo origem.
     * @param destinationId ID do processo destino (ou null para broadcast).
     * @param payload Objeto de domínio a ser transportado.
     * @param timestamp Instante de criação (ms).
     */
    public Message(MessageType type, String senderId, String destinationId, 
                   Object payload, long timestamp) {
        this.messageId = UUID.randomUUID().toString();
        this.type = type;
        this.senderId = senderId;
        this.destinationId = destinationId;
        this.payload = payload;
        this.timestamp = timestamp;
    }
    
    /**
     * Construtor de conveniência que atribui automaticamente o timestamp atual do sistema.
     *
     * @param type Classificação semântica.
     * @param senderId ID do processo origem.
     * @param destinationId ID do processo destino.
     * @param payload Objeto de domínio.
     */
    public Message(MessageType type, String senderId, String destinationId, Object payload) {
        this(type, senderId, destinationId, payload, System.currentTimeMillis());
    }
    
    /**
     * Construtor de conveniência para mensagens de difusão (Broadcast).
     * Define {@code destinationId} como null.
     *
     * @param type Classificação semântica.
     * @param senderId ID do processo origem.
     * @param payload Objeto de domínio.
     */
    public Message(MessageType type, String senderId, Object payload) {
        this(type, senderId, null, payload, System.currentTimeMillis());
    }
    
    //Getters
    
    public String getMessageId() {
        return messageId;
    }
    
    public MessageType getType() {
        return type;
    }
    
    public String getSenderId() {
        return senderId;
    }
    
    public String getDestinationId() {
        return destinationId;
    }
    
    public Object getPayload() {
        return payload;
    }
    
    public long getTimestamp() {
        return timestamp;
    }
    
    /**
     * Verifica se a mensagem se destina a todos os nós da rede.
     *
     * @return {@code true} se o destinationId for nulo.
     */
    public boolean isBroadcast() {
        return destinationId == null;
    }
    
    /**
     * Utilitário para casting seguro e fluente do payload.
     * <p>
     * Evita a necessidade de casts explícitos e supressão de warnings no código cliente.
     *
     * @param <T> O tipo esperado do payload.
     * @param clazz A classe do tipo esperado para verificação em runtime (opcional no uso, mas boa prática).
     * @return O payload convertido para o tipo T.
     * @throws ClassCastException Se o payload não for compatível com o tipo solicitado.
     */
    @SuppressWarnings("unchecked")
    public <T> T getPayloadAs(Class<T> clazz) {
        return (T) payload;
    }
    
    // Impl MessageProtocol interface
    @Override
    public String getSourceNode() {
        return senderId;
    }
    
    @Override
    public String getDestinationNode() {
        return destinationId;
    }
    
    @Override
    public String toString() {
        return String.format("Message[id=%s, type=%s, from=%s, to=%s, timestamp=%d]",
                           messageId, type, senderId, 
                           destinationId != null ? destinationId : "BROADCAST",
                           timestamp);
    }
}