//! `sync_apply` — apply a batch of reconciled diffs in one envelope.
//!
//! Phase 1 stub: records the batch attempt as an EntityAssert on a
//! synthesised `sync_batch:<hash>` entity. Recursive dispatch of the
//! batch's diffs lands once the reconcile pipeline owns its own
//! re-entrancy boundary.

use ontoref_derive::onto_operation;
use ontoref_types::canonical;
use ontoref_types::operation::{OpBody, OpPayload};
use ontoref_types::{AttrId, EntityId, Operation, Value};
use smol_str::SmolStr;

use crate::context::OpContext;
use crate::error::OpError;
use crate::output::{EffectRecord, OpOutput};

#[onto_operation(
    id = "sync_apply",
    description = "Apply a batch of reconciled diffs as a single sync event. Phase 1 records the \
                   batch attempt; recursive dispatch arrives later.",
    validation_sla = "Synchronous",
    precondition = "",
    pre_validators = "",
    effects = "Insert:SyncBatch:diff_count,summary",
    render_paths = "",
    witness_shape = "sync_batch",
    actor_policy = "developer,admin,ci"
)]
fn sync_apply(inputs: &serde_json::Value, ctx: &mut OpContext) -> Result<OpOutput, OpError> {
    let summary = inputs
        .get("summary")
        .and_then(|v| v.as_str())
        .unwrap_or("")
        .to_owned();
    let diff_count = inputs
        .get("diffs")
        .and_then(|v| v.as_array())
        .map(|a| a.len())
        .unwrap_or(0);

    let entity_id = format!("sync_batch:{}", blake3::hash(summary.as_bytes()).to_hex());
    let signing_key = ctx.signing_key.clone();
    let actor = ontoref_types::PublicKey::from_signing_key(&signing_key);
    let timestamp = ctx.next_hlc();
    let op = Operation::sign(
        OpBody {
            actor,
            parents: vec![],
            payload: OpPayload::EntityAssert {
                attrs: vec![
                    (AttrId::new("diff_count"), Value::Int(diff_count as i64)),
                    (AttrId::new("summary"), Value::Str(SmolStr::new(&summary))),
                ],
                entity: EntityId::new(&entity_id),
                valid_from: None,
            },
            timestamp,
        },
        &signing_key,
    );
    ctx.commit_layer.apply(&op);

    let witness_payload =
        canonical::encode(&op).map_err(|e| OpError::body("sync_apply", e.to_string()))?;
    Ok(OpOutput {
        effects: vec![EffectRecord {
            kind: "Insert".to_owned(),
            entity: entity_id,
            attrs: vec!["diff_count".into(), "summary".into()],
            dimensions: vec![],
        }],
        witness_payload,
    })
}
