//! Relationship edge operations and entity accessor methods on `TidalDb`.

use super::TidalDb;
use crate::{
    entities::{
        CreatorItemsBitmap, HardNegIndex, InteractionLedger, PreferenceVectors, RelationshipType,
        UserStateIndex,
    },
    schema::{EntityId, TidalError, Timestamp},
};

impl TidalDb {
    /// Write a relationship edge between two entities.
    ///
    /// The edge is stored in the users keyspace under the `from` entity's key
    /// range using the relationship key encoding.
    ///
    /// # Errors
    ///
    /// - `TidalError::Internal` if no storage backend is wired.
    /// - `TidalError::Storage` on storage engine failure.
    pub fn write_relationship(
        &self,
        from: EntityId,
        rel_type: RelationshipType,
        to: EntityId,
        weight: f64,
        timestamp: Timestamp,
    ) -> crate::Result<()> {
        use crate::entities::relationship::{
            encode_relationship_key, serialize_relationship_value,
        };
        self.require_writeable("write_relationship")?;

        let storage = self.storage()?;

        let key = encode_relationship_key(from, rel_type, to);
        let value = serialize_relationship_value(weight, timestamp);
        storage
            .users_engine()
            .put(&key, &value)
            .map_err(TidalError::from)?;

        // Update in-memory user state based on relationship type.
        match rel_type {
            RelationshipType::Blocks => {
                self.user_state
                    .add_block_creator(from.as_u64(), to.as_u64());
            }
            RelationshipType::Hide => {
                // Route through the observable narrowing helper so an item id
                // above the u32 universe limit is warned, not silently truncated.
                self.user_state.add_hide_item(from.as_u64(), to.as_u64());
            }
            RelationshipType::Follows => {
                // Forward: user -> followed creator
                self.user_state.add_follow(from.as_u64(), to.as_u64());
                // Reverse: creator -> follower users
                self.user_state
                    .add_creator_follower(to.as_u64(), from.as_u64());
            }
            // InteractionWeight edges carry the (user, creator) running score
            // (maintained on the signal path via `persist_interaction_edge`, not
            // here), and Mute is durably persisted but currently inert (no
            // muted-set is applied to retrieval yet — tracked). Explicit arm so
            // the no-op is deliberate and greppable, not absorbed by a catch-all.
            RelationshipType::InteractionWeight | RelationshipType::Mute => {}
        }

        Ok(())
    }

    /// Delete a relationship edge.
    ///
    /// Removes both the durable storage entry and the corresponding in-memory
    /// state (blocked creators, hidden items, follows).
    ///
    /// # Errors
    ///
    /// - `TidalError::Internal` if no storage backend is wired.
    /// - `TidalError::Storage` on storage engine failure.
    pub fn delete_relationship(
        &self,
        from: EntityId,
        rel_type: RelationshipType,
        to: EntityId,
    ) -> crate::Result<()> {
        use crate::entities::relationship::encode_relationship_key;
        self.require_writeable("delete_relationship")?;

        let storage = self.storage()?;
        let key = encode_relationship_key(from, rel_type, to);
        storage
            .users_engine()
            .delete(&key)
            .map_err(TidalError::from)?;

        // Remove corresponding in-memory state.
        match rel_type {
            RelationshipType::Blocks => {
                self.user_state
                    .remove_block_creator(from.as_u64(), to.as_u64());
            }
            RelationshipType::Hide => {
                self.user_state.remove_hide_item(from.as_u64(), to.as_u64());
            }
            RelationshipType::Follows => {
                self.user_state.remove_follow(from.as_u64(), to.as_u64());
                self.user_state
                    .remove_creator_follower(to.as_u64(), from.as_u64());
            }
            // See `write_relationship` for why these are explicit no-ops.
            RelationshipType::InteractionWeight | RelationshipType::Mute => {}
        }

        Ok(())
    }

    /// List all relationship targets for a given (from, `rel_type`) pair.
    ///
    /// Returns `(to_entity_id, weight, timestamp_nanos)` tuples.
    ///
    /// # Errors
    ///
    /// - `TidalError::Internal` if no storage backend is wired.
    /// - `TidalError::Storage` on storage engine failure.
    pub fn list_relationships(
        &self,
        from: EntityId,
        rel_type: RelationshipType,
    ) -> crate::Result<Vec<(EntityId, f64, u64)>> {
        use crate::entities::relationship::{
            deserialize_relationship_value, parse_relationship_to, relationship_prefix,
        };

        let storage = self.storage()?;
        let prefix = relationship_prefix(from, rel_type);
        let mut results = Vec::new();
        for entry in storage.users_engine().scan_prefix(&prefix) {
            let (key, value) = entry.map_err(TidalError::from)?;
            if let Some(to) = parse_relationship_to(&key)
                && let Some((weight, ts_nanos)) = deserialize_relationship_value(&value)
            {
                results.push((to, weight, ts_nanos));
            }
        }
        Ok(results)
    }

    /// Access the per-user state index (seen, blocked, saved, liked, completion).
    #[must_use]
    pub fn user_state(&self) -> &UserStateIndex {
        &self.user_state
    }

    /// Access the creator-items bitmap (maps creators to their item sets).
    #[must_use]
    pub fn creator_items(&self) -> &CreatorItemsBitmap {
        &self.creator_items
    }

    /// Access the hard-negative index.
    #[must_use]
    pub fn hard_negatives(&self) -> &HardNegIndex {
        &self.hard_negatives
    }

    /// Access the interaction weight ledger.
    #[must_use]
    pub fn interaction_ledger(&self) -> &InteractionLedger {
        &self.interaction_ledger
    }

    /// Access the preference vectors store.
    #[must_use]
    pub fn preference_vectors(&self) -> &PreferenceVectors {
        &self.preference_vectors
    }
}
