use crate::codec::Codec;
use crate::error::KnafehError;

/// JSON codec — the default codec for Knafeh.
///
/// Validates that payloads are syntactically valid JSON on encode only
/// (the trust boundary). Decode skips re-validation since the data was
/// either encoded by us or came from a trusted server.
///
/// Uses `serde_json::from_slice::<serde_json::value::RawValue>` for
/// validation — this checks syntax without building a DOM tree,
/// avoiding the allocation overhead of parsing into `Value`.
#[derive(Debug, Clone, Default)]
pub struct JsonCodec;

impl JsonCodec {
    pub fn new() -> Self {
        Self
    }
}

impl Codec for JsonCodec {
    fn encode(&self, value: &[u8]) -> Result<Vec<u8>, KnafehError> {
        // Validate JSON syntax without building a Value tree.
        // RawValue validation is ~2x faster than Value parsing.
        let _ = serde_json::from_slice::<&serde_json::value::RawValue>(value)
            .map_err(|e| KnafehError::Codec(format!("invalid JSON payload: {e}")))?;
        Ok(value.to_vec())
    }

    fn decode(&self, data: &[u8]) -> Result<Vec<u8>, KnafehError> {
        // Validate JSON syntax using RawValue (cheap syntax check,
        // no DOM tree allocation — same as encode).
        let _ = serde_json::from_slice::<&serde_json::value::RawValue>(data)
            .map_err(|e| KnafehError::Codec(format!("invalid JSON payload: {e}")))?;
        Ok(data.to_vec())
    }

    fn content_type(&self) -> &str {
        "application/json"
    }

    fn name(&self) -> &str {
        "json"
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_json_codec_valid() {
        let codec = JsonCodec::new();
        let input = br#"{"hello": "world"}"#;
        let encoded = codec.encode(input).unwrap();
        assert_eq!(encoded, input);
        let decoded = codec.decode(&encoded).unwrap();
        assert_eq!(decoded, input);
    }

    #[test]
    fn test_json_codec_invalid() {
        let codec = JsonCodec::new();
        let result = codec.encode(b"not json {{{");
        assert!(result.is_err());
    }
}
