package sd.serialization;

import java.nio.charset.StandardCharsets;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;

/**
 * Implementação baseada em JSON da estratégia {@link MessageSerializer}, utilizando a biblioteca Gson.
 * <p>
 * Este serializador converte objetos Java para o formato de texto JSON antes da transmissão.
 * Oferece várias vantagens técnicas sobre a serialização nativa do Java:
 * <ul>
 * <li><b>Legibilidade:</b> O formato de texto facilita a depuração (sniffing de rede) sem ferramentas especializadas.</li>
 * <li><b>Interoperabilidade:</b> Permite futura integração com componentes não-Java (ex: Dashboards web em JS).</li>
 * <li><b>Segurança:</b> Reduz a superfície de ataque para execução remota de código (RCE), pois não desserializa classes arbitrárias, apenas dados.</li>
 * </ul>
 * <p>
 * <b>Thread-Safety:</b> A instância interna do {@code Gson} é imutável e thread-safe, permitindo
 * que este serializador seja partilhado entre múltiplas threads (ex: no pool do DashboardServer).
 * * @see MessageSerializer
 */
public class JsonMessageSerializer implements MessageSerializer {
    
    private final Gson gson;
    private final boolean prettyPrint;
    
    /**
     * Cria um novo serializador JSON com configuração otimizada para produção (compacto).
     */
    public JsonMessageSerializer() {
        this(false);
    }
    
    /**
     * Cria um novo serializador JSON com formatação opcional.
     * * @param prettyPrint Se {@code true}, o JSON gerado incluirá indentação e quebras de linha.
     * Útil para debug, mas aumenta significativamente o tamanho do payload.
     */
    public JsonMessageSerializer(boolean prettyPrint) {
        this.prettyPrint = prettyPrint;
        GsonBuilder builder = new GsonBuilder();
        
        if (prettyPrint) {
            builder.setPrettyPrinting();
        }
        
        // Register custom type adapters here if needed
        // builder.registerTypeAdapter(Vehicle.class, new VehicleAdapter());
        
        this.gson = builder.create();
    }
    
    /**
     * Converte um objeto em memória para um array de bytes JSON (UTF-8).
     *
     * @param object O objeto a ser serializado.
     * @return O payload em bytes pronto para transmissão TCP.
     * @throws SerializationException Se o objeto não for compatível com JSON ou ocorrer erro de encoding.
     */
    @Override
    public byte[] serialize(Object object) throws SerializationException {
        if (object == null) {
            throw new IllegalArgumentException("Cannot serialize null object");
        }
        
        try {
            String json = gson.toJson(object);
            return json.getBytes(StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new SerializationException(
                "Failed to serialize object of type " + object.getClass().getName(), e);
        }
    }
    
    /**
     * Reconstrói um objeto Java a partir de um array de bytes JSON.
     * <p>
     * Realiza a validação sintática do JSON e a validação de tipo baseada na classe alvo.
     *
     * @param data O array de bytes recebido da rede.
     * @param clazz A classe do objeto esperado (Type Token).
     * @return A instância do objeto reconstruído.
     * @throws SerializationException Se o JSON for malformado ou incompatível com a classe alvo.
     */
    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException {
        if (data == null) {
            throw new IllegalArgumentException("Cannot deserialize null data");
        }
        if (clazz == null) {
            throw new IllegalArgumentException("Class type cannot be null");
        }
        
        try {
            String json = new String(data, StandardCharsets.UTF_8);
            return gson.fromJson(json, clazz);
        } catch (JsonSyntaxException e) {
            throw new SerializationException(
                "Failed to parse JSON for type " + clazz.getName(), e);
        } catch (Exception e) {
            throw new SerializationException(
                "Failed to deserialize object of type " + clazz.getName(), e);
        }
    }
    
    @Override
    public String getName() {
        return "JSON (Gson)";
    }
    
    /**
     * Retorna a instância subjacente do Gson para configurações avançadas.
     * * @return A instância Gson configurada.
     */
    public Gson getGson() {
        return gson;
    }
    
    /**
     * Verifica se a formatação "pretty print" está ativa.
     * * @return true se a indentação estiver habilitada.
     */
    public boolean isPrettyPrint() {
        return prettyPrint;
    }
}