//! Networked entity components
//!
//! This module defines components that mark entities as networked and track
//! their network identity across the distributed system.

use bevy::prelude::*;
use serde::{
    Deserialize,
    Serialize,
};

use crate::networking::vector_clock::NodeId;

/// Marker component indicating an entity should be synchronized over the
/// network
///
/// Add this component to any entity that should have its state synchronized
/// across peers. The networking system will automatically track changes and
/// broadcast deltas.
///
/// # Relationship with Persisted
///
/// NetworkedEntity and Persisted are complementary:
/// - `Persisted` - Entity state saved to local SQLite database
/// - `NetworkedEntity` - Entity state synchronized across network peers
///
/// Most entities will have both components for full durability and sync.
///
/// # Network Identity
///
/// Each networked entity has:
/// - `network_id` - Globally unique UUID for this entity across all peers
/// - `owner_node_id` - Node that originally created this entity
///
/// # Example
///
/// ```
/// use bevy::prelude::*;
/// use libmarathon::networking::NetworkedEntity;
/// use uuid::Uuid;
///
/// fn spawn_networked_entity(mut commands: Commands) {
///     let node_id = Uuid::new_v4();
///
///     commands.spawn((NetworkedEntity::new(node_id), Transform::default()));
/// }
/// ```
#[derive(Component, Reflect, Debug, Clone, Serialize, Deserialize)]
#[reflect(Component)]
pub struct NetworkedEntity {
    /// Globally unique network ID for this entity
    ///
    /// This ID is used to identify the entity across all peers in the network.
    /// When a peer receives an EntityDelta, it uses this ID to locate the
    /// corresponding local entity.
    pub network_id: uuid::Uuid,

    /// Node that created this entity
    ///
    /// Used for conflict resolution and ownership tracking. When two nodes
    /// concurrently create entities, the owner_node_id can be used as a
    /// tiebreaker.
    pub owner_node_id: NodeId,
}

impl NetworkedEntity {
    /// Create a new networked entity
    ///
    /// Generates a new random network_id and sets the owner to the specified
    /// node.
    ///
    /// # Example
    ///
    /// ```
    /// use libmarathon::networking::NetworkedEntity;
    /// use uuid::Uuid;
    ///
    /// let node_id = Uuid::new_v4();
    /// let entity = NetworkedEntity::new(node_id);
    ///
    /// assert_eq!(entity.owner_node_id, node_id);
    /// ```
    pub fn new(owner_node_id: NodeId) -> Self {
        Self {
            network_id: uuid::Uuid::new_v4(),
            owner_node_id,
        }
    }

    /// Create a networked entity with a specific network ID
    ///
    /// Used when receiving entities from remote peers - we need to use their
    /// network_id rather than generating a new one.
    ///
    /// # Example
    ///
    /// ```
    /// use libmarathon::networking::NetworkedEntity;
    /// use uuid::Uuid;
    ///
    /// let network_id = Uuid::new_v4();
    /// let owner_id = Uuid::new_v4();
    /// let entity = NetworkedEntity::with_id(network_id, owner_id);
    ///
    /// assert_eq!(entity.network_id, network_id);
    /// assert_eq!(entity.owner_node_id, owner_id);
    /// ```
    pub fn with_id(network_id: uuid::Uuid, owner_node_id: NodeId) -> Self {
        Self {
            network_id,
            owner_node_id,
        }
    }

    /// Check if this node owns the entity
    pub fn is_owned_by(&self, node_id: NodeId) -> bool {
        self.owner_node_id == node_id
    }
}

impl Default for NetworkedEntity {
    fn default() -> Self {
        Self {
            network_id: uuid::Uuid::new_v4(),
            owner_node_id: uuid::Uuid::new_v4(),
        }
    }
}

/// Wrapper for Transform component that enables CRDT synchronization
///
/// This is a marker component used alongside Transform to indicate that
/// Transform changes should be synchronized using Last-Write-Wins semantics.
///
/// # Example
///
/// ```
/// use bevy::prelude::*;
/// use libmarathon::networking::{
///     NetworkedEntity,
///     NetworkedTransform,
/// };
/// use uuid::Uuid;
///
/// fn spawn_synced_transform(mut commands: Commands) {
///     let node_id = Uuid::new_v4();
///
///     commands.spawn((
///         NetworkedEntity::new(node_id),
///         Transform::default(),
///         NetworkedTransform,
///     ));
/// }
/// ```
#[derive(Component, Reflect, Debug, Clone, Copy, Default)]
#[reflect(Component)]
pub struct NetworkedTransform;

/// Wrapper for a selection component using OR-Set semantics
///
/// Tracks a set of selected entity network IDs. Uses OR-Set (Observed-Remove)
/// CRDT to handle concurrent add/remove operations correctly.
///
/// # OR-Set Semantics
///
/// - Concurrent adds and removes: add wins
/// - Each add has a unique operation ID
/// - Removes reference specific add operation IDs
///
/// # Example
///
/// ```
/// use bevy::prelude::*;
/// use libmarathon::networking::{
///     NetworkedEntity,
///     NetworkedSelection,
/// };
/// use uuid::Uuid;
///
/// fn create_selection(mut commands: Commands) {
///     let node_id = Uuid::new_v4();
///     let mut selection = NetworkedSelection::new();
///
///     // Add some entities to the selection
///     selection.selected_ids.insert(Uuid::new_v4());
///     selection.selected_ids.insert(Uuid::new_v4());
///
///     commands.spawn((NetworkedEntity::new(node_id), selection));
/// }
/// ```
#[derive(Component, Reflect, Debug, Clone, Default)]
#[reflect(Component)]
pub struct NetworkedSelection {
    /// Set of selected entity network IDs
    ///
    /// This will be synchronized using OR-Set CRDT semantics in later phases.
    /// For now, it's a simple HashSet.
    pub selected_ids: std::collections::HashSet<uuid::Uuid>,
}

impl NetworkedSelection {
    /// Create a new empty selection
    pub fn new() -> Self {
        Self {
            selected_ids: std::collections::HashSet::new(),
        }
    }

    /// Add an entity to the selection
    pub fn add(&mut self, entity_id: uuid::Uuid) {
        self.selected_ids.insert(entity_id);
    }

    /// Remove an entity from the selection
    pub fn remove(&mut self, entity_id: uuid::Uuid) {
        self.selected_ids.remove(&entity_id);
    }

    /// Check if an entity is selected
    pub fn contains(&self, entity_id: uuid::Uuid) -> bool {
        self.selected_ids.contains(&entity_id)
    }

    /// Clear all selections
    pub fn clear(&mut self) {
        self.selected_ids.clear();
    }

    /// Get the number of selected entities
    pub fn len(&self) -> usize {
        self.selected_ids.len()
    }

    /// Check if the selection is empty
    pub fn is_empty(&self) -> bool {
        self.selected_ids.is_empty()
    }
}

/// Wrapper for a drawing path component using Sequence CRDT semantics
///
/// Represents an ordered sequence of points that can be collaboratively edited.
/// Uses RGA (Replicated Growable Array) CRDT to maintain consistent ordering
/// across concurrent insertions.
///
/// # RGA Semantics
///
/// - Each point has a unique operation ID
/// - Points reference the ID of the point they're inserted after
/// - Concurrent insertions maintain consistent ordering
///
/// # Example
///
/// ```
/// use bevy::prelude::*;
/// use libmarathon::networking::{
///     NetworkedDrawingPath,
///     NetworkedEntity,
/// };
/// use uuid::Uuid;
///
/// fn create_path(mut commands: Commands) {
///     let node_id = Uuid::new_v4();
///     let mut path = NetworkedDrawingPath::new();
///
///     // Add some points to the path
///     path.points.push(Vec2::new(0.0, 0.0));
///     path.points.push(Vec2::new(10.0, 10.0));
///     path.points.push(Vec2::new(20.0, 5.0));
///
///     commands.spawn((NetworkedEntity::new(node_id), path));
/// }
/// ```
#[derive(Component, Reflect, Debug, Clone, Default)]
#[reflect(Component)]
pub struct NetworkedDrawingPath {
    /// Ordered sequence of points in the path
    ///
    /// This will be synchronized using RGA (Sequence CRDT) semantics in later
    /// phases. For now, it's a simple Vec.
    pub points: Vec<Vec2>,

    /// Drawing stroke color
    pub color: Color,

    /// Stroke width
    pub width: f32,
}

impl NetworkedDrawingPath {
    /// Create a new empty drawing path
    pub fn new() -> Self {
        Self {
            points: Vec::new(),
            color: Color::BLACK,
            width: 2.0,
        }
    }

    /// Create a path with a specific color and width
    pub fn with_style(color: Color, width: f32) -> Self {
        Self {
            points: Vec::new(),
            color,
            width,
        }
    }

    /// Add a point to the end of the path
    pub fn push(&mut self, point: Vec2) {
        self.points.push(point);
    }

    /// Get the number of points in the path
    pub fn len(&self) -> usize {
        self.points.len()
    }

    /// Check if the path is empty
    pub fn is_empty(&self) -> bool {
        self.points.is_empty()
    }

    /// Clear all points from the path
    pub fn clear(&mut self) {
        self.points.clear();
    }
}

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

    #[test]
    fn test_networked_entity_new() {
        let node_id = uuid::Uuid::new_v4();
        let entity = NetworkedEntity::new(node_id);

        assert_eq!(entity.owner_node_id, node_id);
        assert_ne!(entity.network_id, uuid::Uuid::nil());
    }

    #[test]
    fn test_networked_entity_with_id() {
        let network_id = uuid::Uuid::new_v4();
        let owner_id = uuid::Uuid::new_v4();
        let entity = NetworkedEntity::with_id(network_id, owner_id);

        assert_eq!(entity.network_id, network_id);
        assert_eq!(entity.owner_node_id, owner_id);
    }

    #[test]
    fn test_networked_entity_is_owned_by() {
        let owner_id = uuid::Uuid::new_v4();
        let other_id = uuid::Uuid::new_v4();
        let entity = NetworkedEntity::new(owner_id);

        assert!(entity.is_owned_by(owner_id));
        assert!(!entity.is_owned_by(other_id));
    }

    #[test]
    fn test_networked_selection() {
        let mut selection = NetworkedSelection::new();
        let id1 = uuid::Uuid::new_v4();
        let id2 = uuid::Uuid::new_v4();

        assert!(selection.is_empty());

        selection.add(id1);
        assert_eq!(selection.len(), 1);
        assert!(selection.contains(id1));

        selection.add(id2);
        assert_eq!(selection.len(), 2);
        assert!(selection.contains(id2));

        selection.remove(id1);
        assert_eq!(selection.len(), 1);
        assert!(!selection.contains(id1));

        selection.clear();
        assert!(selection.is_empty());
    }

    #[test]
    fn test_networked_drawing_path() {
        let mut path = NetworkedDrawingPath::new();

        assert!(path.is_empty());

        path.push(Vec2::new(0.0, 0.0));
        assert_eq!(path.len(), 1);

        path.push(Vec2::new(10.0, 10.0));
        assert_eq!(path.len(), 2);

        path.clear();
        assert!(path.is_empty());
    }

    #[test]
    fn test_drawing_path_with_style() {
        let path = NetworkedDrawingPath::with_style(Color::srgb(1.0, 0.0, 0.0), 5.0);

        assert_eq!(path.color, Color::srgb(1.0, 0.0, 0.0));
        assert_eq!(path.width, 5.0);
    }
}
