use std::path::Path;

use crate::utils::{DGGSCell, Hash, TransformRecord};

pub fn build_transform_record(
    step: &str,
    params: &serde_json::Value,
    in_hash: &Hash,
    out_hash: &Hash,
    cell: &DGGSCell,
) -> TransformRecord {
    TransformRecord {
        step: step.to_string(),
        params: params.clone(),
        in_hash: in_hash.clone(),
        out_hash: out_hash.clone(),
        cell: cell.clone(),
    }
}

pub fn serialize_transform_log(log: &[TransformRecord]) -> Result<String, serde_json::Error> {
    serde_json::to_string_pretty(log)
}

pub fn compute_meta_hash(json: &str) -> Hash {
    let h = blake3::hash(json.as_bytes());
    Hash(*h.as_bytes())
}

pub fn write_transform_manifest(path: &Path, log: &[TransformRecord]) -> std::io::Result<()> {
    let json = serialize_transform_log(log).map_err(std::io::Error::other)?;
    std::fs::write(path, json)
}

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

    #[test]
    fn new_record_copies_fields() {
        let params = serde_json::json!({"a": 1});
        let in_hash = Hash([1u8; 32]);
        let out_hash = Hash([2u8; 32]);
        let cell = DGGSCell { id: 9 };
        let record = build_transform_record("step", &params, &in_hash, &out_hash, &cell);
        assert_eq!(record.step, "step");
        assert_eq!(record.params, params);
        assert_eq!(record.in_hash, in_hash);
        assert_eq!(record.out_hash, out_hash);
        assert_eq!(record.cell.id, 9);
    }

    #[test]
    fn serialize_log_contains_step_name() {
        let log = vec![TransformRecord {
            step: "x".to_string(),
            params: serde_json::json!({}),
            in_hash: Hash([0u8; 32]),
            out_hash: Hash([1u8; 32]),
            cell: DGGSCell { id: 0 },
        }];
        let json = serialize_transform_log(&log).expect("serialize log");
        assert!(json.contains("\"step\": \"x\""));
    }

    #[test]
    fn compute_meta_hash_is_deterministic() {
        let a = compute_meta_hash("{\"k\":1}");
        let b = compute_meta_hash("{\"k\":1}");
        assert_eq!(a, b);
    }

    #[test]
    fn write_manifest_writes_file() {
        let file = NamedTempFile::new().expect("temp file");
        let log = vec![TransformRecord {
            step: "write".to_string(),
            params: serde_json::json!({}),
            in_hash: Hash([0u8; 32]),
            out_hash: Hash([1u8; 32]),
            cell: DGGSCell { id: 0 },
        }];
        write_transform_manifest(file.path(), &log).expect("write manifest");
        let written = std::fs::read_to_string(file.path()).expect("read manifest");
        assert!(written.contains("write"));
    }
}
