//! Wire-form snapshot of a `BoardGraph`. Sent at session-init so the
//! client reconstructs the graph in one go; subsequent mutations
//! flow as `GameEvent::GraphMutated` events.

use std::collections::BTreeMap;
use crate::board::{BoardGraph, EdgeKind, FaceCoord};
use crate::board::ids::{EdgeId, FaceId, BoardNodeId};

#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BoardGraphSnapshot {
    pub name: String,
    pub nodes: Vec<BoardNodeId>,
    pub edges: BTreeMap<EdgeId, EdgeKind>,
    pub faces: BTreeMap<FaceId, Vec<BoardNodeId>>,
    pub next_node_id: u32,
    pub next_edge_id: u32,
    pub next_face_id: u32,
    /// AR-1: per-face coordinate metadata. Empty map if the source graph
    /// was built by a topology-only path. Defaulted on deserialize for
    /// backwards-compat with existing snapshots.
    #[cfg_attr(feature = "serde", serde(default))]
    pub face_coords: BTreeMap<FaceId, FaceCoord>,
}

impl BoardGraphSnapshot {
    pub fn from_graph(name: impl Into<String>, g: &BoardGraph) -> Self {
        Self {
            name: name.into(),
            nodes: g.nodes.iter().copied().collect(),
            edges: g.edges.clone(),
            faces: g.faces.clone(),
            next_node_id: g.next_node_id,
            next_edge_id: g.next_edge_id,
            next_face_id: g.next_face_id,
            face_coords: g.face_coords.clone(),
        }
    }

    pub fn into_graph(self) -> BoardGraph {
        // Reconstruct the reverse-lookup index from face_coords on restore.
        let coord_to_face: BTreeMap<FaceCoord, FaceId> =
            self.face_coords.iter().map(|(id, c)| (*c, *id)).collect();
        BoardGraph {
            nodes: self.nodes.into_iter().collect(),
            edges: self.edges,
            faces: self.faces,
            next_node_id: self.next_node_id,
            next_edge_id: self.next_edge_id,
            next_face_id: self.next_face_id,
            face_coords: self.face_coords,
            coord_to_face,
        }
    }
}

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

    #[test]
    fn snapshot_round_trip_preserves_graph() {
        let b = BoardBuilder::square_grid(3, 3);
        let snap = BoardGraphSnapshot::from_graph(&b.name, &b.graph);
        let restored = snap.clone().into_graph();
        assert_eq!(restored.node_count(), b.graph.node_count());
        assert_eq!(restored.edge_count(), b.graph.edge_count());
        assert_eq!(restored.face_count(), b.graph.face_count());
        assert_eq!(restored.next_node_id, b.graph.next_node_id);
    }

    #[test]
    #[cfg(feature = "wire")]
    fn snapshot_postcard_round_trip() {
        let b = BoardBuilder::linear_path(4);
        let snap = BoardGraphSnapshot::from_graph(&b.name, &b.graph);
        let bytes = postcard::to_allocvec(&snap).unwrap();
        let decoded: BoardGraphSnapshot = postcard::from_bytes(&bytes).unwrap();
        assert_eq!(decoded, snap);
    }
}
