use crate::ccr::decode::{CcrDecodeError, decode_content_info};
use serde_json::json;

#[derive(Debug, thiserror::Error)]
pub enum CcrDumpError {
    #[error("CCR decode failed: {0}")]
    Decode(#[from] CcrDecodeError),

    #[error("format time failed: {0}")]
    FormatTime(String),
}

pub fn dump_content_info_json_value(der: &[u8]) -> Result<serde_json::Value, CcrDumpError> {
    let content_info = decode_content_info(der)?;
    dump_content_info_json(&content_info)
}

pub fn dump_content_info_json(
    content_info: &crate::ccr::model::CcrContentInfo,
) -> Result<serde_json::Value, CcrDumpError> {
    let produced_at = content_info
        .content
        .produced_at
        .to_offset(time::UtcOffset::UTC)
        .format(&time::format_description::well_known::Rfc3339)
        .map_err(|e| CcrDumpError::FormatTime(e.to_string()))?;

    let mfts = content_info.content.mfts.as_ref().map(|state| {
        json!({
            "present": true,
            "manifest_instances": state.mis.len(),
            "most_recent_update_rfc3339_utc": state.most_recent_update.to_offset(time::UtcOffset::UTC).format(&time::format_description::well_known::Rfc3339).unwrap(),
            "hash_hex": hex::encode(&state.hash),
        })
    }).unwrap_or_else(|| json!({"present": false}));

    let vrps_total = content_info
        .content
        .vrps
        .as_ref()
        .map(|state| {
            state
                .rps
                .iter()
                .map(|set| set.ip_addr_blocks.len())
                .sum::<usize>()
        })
        .unwrap_or(0);
    let vrps = content_info
        .content
        .vrps
        .as_ref()
        .map(|state| {
            json!({
                "present": true,
                "payload_sets": state.rps.len(),
                "hash_hex": hex::encode(&state.hash),
                "ip_addr_block_count": vrps_total,
            })
        })
        .unwrap_or_else(|| json!({"present": false}));

    let vaps = content_info
        .content
        .vaps
        .as_ref()
        .map(|state| {
            json!({
                "present": true,
                "payload_sets": state.aps.len(),
                "hash_hex": hex::encode(&state.hash),
                "provider_count": state.aps.iter().map(|set| set.providers.len()).sum::<usize>(),
            })
        })
        .unwrap_or_else(|| json!({"present": false}));

    let tas = content_info
        .content
        .tas
        .as_ref()
        .map(|state| {
            json!({
                "present": true,
                "ski_count": state.skis.len(),
                "hash_hex": hex::encode(&state.hash),
            })
        })
        .unwrap_or_else(|| json!({"present": false}));

    let rks = content_info.content.rks.as_ref().map(|state| {
        json!({
            "present": true,
            "router_key_sets": state.rksets.len(),
            "router_key_count": state.rksets.iter().map(|set| set.router_keys.len()).sum::<usize>(),
            "hash_hex": hex::encode(&state.hash),
        })
    }).unwrap_or_else(|| json!({"present": false}));

    Ok(json!({
        "content_type_oid": content_info.content_type_oid,
        "version": content_info.content.version,
        "hash_alg": content_info.content.hash_alg.oid(),
        "produced_at_rfc3339_utc": produced_at,
        "state_aspects": {
            "mfts": mfts,
            "vrps": vrps,
            "vaps": vaps,
            "tas": tas,
            "rks": rks,
        }
    }))
}
