//! Creator entity write/read operations on `TidalDb`.

use std::collections::HashMap;

use super::TidalDb;
use crate::{
    schema::{EntityId, EntityKind, TidalError},
    storage::vector::deserialize_embedding,
};

impl TidalDb {
    /// Write (or overwrite) a creator entity.
    ///
    /// # Errors
    ///
    /// - `TidalError::Internal` if no storage backend is wired.
    /// - `TidalError::Storage` on storage engine failure.
    pub fn write_creator(
        &self,
        id: EntityId,
        metadata: &HashMap<String, String>,
    ) -> crate::Result<()> {
        self.require_writeable("write_creator")?;
        let storage = self.storage()?;
        super::users::write_entity_meta(storage.creators_engine(), id, metadata)?;

        // Enqueue for the async creator text syncer (best-effort). The creator
        // entity store is the source of truth; the text index is derived state,
        // and any write the syncer has not yet committed before a crash is
        // recovered by the unconditional rebuild-from-store at open (see
        // `crate::db::state_rebuild::rebuild_text_indexes_at_open`).
        //
        // Clone the Sender out of the guard and DROP the guard before enqueuing,
        // then use the non-blocking `try_send`. Holding `creator_text_tx` across
        // a blocking `send` would stall every concurrent creator write once the
        // bounded outbox fills under a sustained Tantivy commit outage — the same
        // hazard the item write path avoids. The durable write above has already
        // completed, so dropping the enqueue costs zero durability: the
        // rebuild-from-store at open re-indexes anything dropped.
        let tx = self
            .creator_text_tx
            .lock()
            .ok()
            .and_then(|guard| guard.as_ref().cloned());
        if let Some(tx) = tx
            && let Err(e) = tx.try_send(crate::text::PendingWrite {
                entity_id: id,
                metadata: metadata.clone(),
                deleted: false,
            })
        {
            tracing::warn!(
                entity_id = id.as_u64(),
                error = %e,
                "creator text-index outbox enqueue dropped (syncer stalled or gone); \
                 rebuild-from-store at open will re-index this creator"
            );
        }

        Ok(())
    }

    /// Read creator metadata for a given entity ID.
    ///
    /// Returns `None` if the creator does not exist in storage.
    ///
    /// # Errors
    ///
    /// - `TidalError::Internal` if no storage backend is wired.
    /// - `TidalError::Storage` on storage engine failure.
    pub fn get_creator_metadata(
        &self,
        id: EntityId,
    ) -> crate::Result<Option<HashMap<String, String>>> {
        let storage = self.storage()?;
        super::users::read_entity_meta(storage.creators_engine(), id)
    }

    /// Write (or overwrite) a pre-computed embedding for a creator entity.
    ///
    /// L2-normalizes the embedding, stores it in the creator entity store (source
    /// of truth), and inserts it into the `(EntityKind::Creator, "content")` HNSW
    /// slot. The slot is auto-registered if not yet present, using
    /// `embedding.len()` as the dimension count.
    ///
    /// # Errors
    ///
    /// - `TidalError::Internal` if no storage backend is wired or lock is poisoned.
    /// - `TidalError::Storage` on storage engine failure.
    /// - `TidalError::Internal` if the embedding has zero norm.
    pub fn write_creator_embedding(&self, id: EntityId, embedding: &[f32]) -> crate::Result<()> {
        self.require_writeable("write_creator_embedding")?;
        let storage = self.storage()?;
        self.write_entity_embedding(
            id,
            EntityKind::Creator,
            "content",
            embedding,
            storage.creators_engine(),
            "write_creator_embedding",
        )
    }

    /// Read a stored creator embedding for a given entity ID.
    ///
    /// Returns `None` if no embedding has been written for this creator.
    ///
    /// # Errors
    ///
    /// - `TidalError::Internal` if no storage backend is wired.
    /// - `TidalError::Storage` on storage engine failure.
    pub fn read_creator_embedding(&self, id: EntityId) -> crate::Result<Option<Vec<f32>>> {
        let storage = self.storage()?;
        let key = crate::storage::vector::embedding_store_key(id, "content");
        match storage.creators_engine().get(&key) {
            Ok(Some(bytes)) => {
                let v = deserialize_embedding(&bytes)
                    .map_err(|e| TidalError::internal("read_creator_embedding", format!("{e}")))?;
                Ok(Some(v))
            }
            Ok(None) => Ok(None),
            Err(e) => Err(TidalError::from(e)),
        }
    }
}
