//! Construction-time mutation primitives on `BoardGraph`. These run
//! before the session starts; they allocate IDs from internal counters
//! and return them immediately. No event pipeline involved here.

#[allow(unused_imports)]
use std::collections::{BTreeMap, BTreeSet};
#[allow(unused_imports)]
use crate::board::edge::EdgeKind;
#[allow(unused_imports)]
use crate::board::ids::{EdgeId, FaceId, BoardNodeId};

#[cfg(test)]
mod tests {
    use crate::board::{BoardGraph, EdgeKind};
    use crate::board::ids::{EdgeId, BoardNodeId};

    #[test]
    fn new_graph_is_empty() {
        let g = BoardGraph::new();
        assert_eq!(g.node_count(), 0);
        assert_eq!(g.edge_count(), 0);
        assert_eq!(g.face_count(), 0);
    }

    #[test]
    fn add_node_returns_increasing_ids() {
        let mut g = BoardGraph::new();
        let a = g.add_node();
        let b = g.add_node();
        let c = g.add_node();
        assert_eq!(a, BoardNodeId(0));
        assert_eq!(b, BoardNodeId(1));
        assert_eq!(c, BoardNodeId(2));
        assert_eq!(g.node_count(), 3);
    }

    #[test]
    fn contains_node_after_add() {
        let mut g = BoardGraph::new();
        let id = g.add_node();
        assert!(g.contains_node(id));
        assert!(!g.contains_node(BoardNodeId(999)));
    }

    #[test]
    fn add_bidirectional_edge_records_kind() {
        let mut g = BoardGraph::new();
        let a = g.add_node();
        let b = g.add_node();
        let e = g.add_bidirectional_edge(a, b);
        let kind = g.edge_kind(e).unwrap();
        match kind {
            EdgeKind::Bidirectional { a: x, b: y } => {
                assert!((*x == a && *y == b) || (*x == b && *y == a));
            }
            _ => panic!("expected Bidirectional, got {:?}", kind),
        }
    }

    #[test]
    fn add_directed_edge_preserves_from_to_order() {
        let mut g = BoardGraph::new();
        let a = g.add_node();
        let b = g.add_node();
        let e = g.add_directed_edge(a, b);
        assert_eq!(*g.edge_kind(e).unwrap(), EdgeKind::Directed { from: a, to: b });
    }

    #[test]
    fn add_edge_returns_increasing_ids() {
        let mut g = BoardGraph::new();
        let a = g.add_node();
        let b = g.add_node();
        let c = g.add_node();
        let e1 = g.add_bidirectional_edge(a, b);
        let e2 = g.add_directed_edge(b, c);
        assert_eq!(e1, EdgeId(0));
        assert_eq!(e2, EdgeId(1));
    }

    #[test]
    fn add_face_with_closed_triangle() {
        let mut g = BoardGraph::new();
        let a = g.add_node();
        let b = g.add_node();
        let c = g.add_node();
        g.add_bidirectional_edge(a, b);
        g.add_bidirectional_edge(b, c);
        g.add_bidirectional_edge(c, a);
        let f = g.add_face(vec![a, b, c]).unwrap();
        assert_eq!(g.face_bounding(f), Some(&vec![a, b, c]));
    }

    #[test]
    fn try_add_face_rejects_open_boundary() {
        let mut g = BoardGraph::new();
        let a = g.add_node();
        let b = g.add_node();
        let c = g.add_node();
        // Edge a-b and b-c, but NO edge c-a (boundary not closed).
        g.add_bidirectional_edge(a, b);
        g.add_bidirectional_edge(b, c);
        let res = g.try_add_face(vec![a, b, c]);
        assert!(matches!(res, Err(crate::board::GraphError::OpenBoundary { .. })));
    }

    #[test]
    fn try_add_face_rejects_unknown_node() {
        let mut g = BoardGraph::new();
        let a = g.add_node();
        let b = g.add_node();
        let bogus = BoardNodeId(999);
        let res = g.try_add_face(vec![a, b, bogus]);
        assert!(matches!(res, Err(crate::board::GraphError::UnknownNode(_))));
    }
}
