//! `close_dev_session` — close an open code-development session.
//!
//! Mutates `status=Closed` on the session entity and records the drift
//! summary the caller supplies (e.g. files touched outside declared
//! scope). The op is the post-hoc audit trail that `start_dev_session`
//! anticipates.

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 = "close_dev_session",
    description = "Close an open dev session. Mutates status=Closed and records observed drift.",
    validation_sla = "Synchronous",
    precondition = "",
    pre_validators = "",
    effects = "Update:DevSession:status,drift_summary",
    render_paths = "",
    witness_shape = "dev_session_close",
    actor_policy = "developer,admin"
)]
fn close_dev_session(inputs: &serde_json::Value, ctx: &mut OpContext) -> Result<OpOutput, OpError> {
    let session_id = inputs
        .get("session_id")
        .and_then(|v| v.as_str())
        .ok_or_else(|| OpError::body("close_dev_session", "missing 'session_id'"))?
        .to_owned();
    let drift = inputs
        .get("drift_summary")
        .and_then(|v| v.as_str())
        .unwrap_or("")
        .to_owned();

    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("status"), Value::Str(SmolStr::new("Closed"))),
                    (
                        AttrId::new("drift_summary"),
                        Value::Str(SmolStr::new(&drift)),
                    ),
                ],
                entity: EntityId::new(&session_id),
                valid_from: None,
            },
            timestamp,
        },
        &signing_key,
    );
    ctx.commit_layer.apply(&op);

    let witness_payload =
        canonical::encode(&op).map_err(|e| OpError::body("close_dev_session", e.to_string()))?;
    Ok(OpOutput {
        effects: vec![EffectRecord {
            kind: "Update".to_owned(),
            entity: session_id,
            attrs: vec!["status".into(), "drift_summary".into()],
            dimensions: vec![],
        }],
        witness_payload,
    })
}
