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

use std::collections::HashMap;

use crate::schema::{EntityId, EntityKind, TidalError};
use crate::storage::vector::registry::{EmbeddingSlotState, EmbeddingSource, HnswParams};
use crate::storage::vector::{
    BruteForceIndex, QuantizationLevel, VectorIndexConfig, deserialize_embedding, insert_embedding,
};
use crate::storage::{Tag, encode_key};

use super::TidalDb;

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()?;
        let key = encode_key(id, Tag::Meta, b"");
        let value = crate::entities::serialize_entity(None, metadata);
        storage
            .creators_engine()
            .put(&key, &value)
            .map_err(TidalError::from)?;

        // Enqueue for creator text index (best-effort).
        if let Ok(guard) = self.creator_text_tx.lock()
            && let Some(tx) = guard.as_ref()
        {
            let _ = tx.send(crate::text::PendingWrite {
                entity_id: id,
                metadata: metadata.clone(),
                seq: 0,
                deleted: false,
            });
        }

        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()?;
        let key = encode_key(id, Tag::Meta, b"");
        match storage.creators_engine().get(&key) {
            Ok(Some(bytes)) => {
                let (_emb, meta) = crate::entities::deserialize_entity(&bytes);
                Ok(Some(meta))
            }
            Ok(None) => Ok(None),
            Err(e) => Err(TidalError::from(e)),
        }
    }

    /// 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.
    #[allow(clippy::significant_drop_tightening)] // lock must be held across insert_embedding call
    pub fn write_creator_embedding(&self, id: EntityId, embedding: &[f32]) -> crate::Result<()> {
        self.require_writeable("write_creator_embedding")?;
        let storage = self.storage()?;

        // Auto-register the creator "content" slot if absent.
        {
            let mut registry = self.embedding_registry.write().map_err(|_| {
                TidalError::internal(
                    "write_creator_embedding",
                    "embedding_registry write lock poisoned",
                )
            })?;
            if registry.get(EntityKind::Creator, "content").is_none() {
                let config = VectorIndexConfig {
                    dimensions: embedding.len(),
                    ..VectorIndexConfig::default()
                };
                let state = EmbeddingSlotState {
                    index: Box::new(BruteForceIndex::new(config)),
                    dimensions: embedding.len(),
                    quantization: QuantizationLevel::F32,
                    source: EmbeddingSource::External,
                    params: HnswParams::default(),
                };
                registry
                    .register(EntityKind::Creator, "content".to_string(), state)
                    .map_err(|e| {
                        TidalError::internal(
                            "write_creator_embedding",
                            format!("slot registration failed: {e}"),
                        )
                    })?;
            }
        }

        // Insert into the HNSW index and store in entity engine.
        // Read the slot dimensions first, dropping the lock before the storage write.
        let dimensions = {
            let registry = self.embedding_registry.read().map_err(|_| {
                TidalError::internal(
                    "write_creator_embedding",
                    "embedding_registry read lock poisoned",
                )
            })?;
            registry
                .get(EntityKind::Creator, "content")
                .ok_or_else(|| {
                    TidalError::internal("write_creator_embedding", "creator content slot missing")
                })?
                .dimensions
        };
        // Re-acquire read lock to get the index reference, perform the insert,
        // then drop the lock.
        {
            let registry = self.embedding_registry.read().map_err(|_| {
                TidalError::internal(
                    "write_creator_embedding",
                    "embedding_registry read lock poisoned",
                )
            })?;
            let slot = registry
                .get(EntityKind::Creator, "content")
                .ok_or_else(|| {
                    TidalError::internal("write_creator_embedding", "creator content slot missing")
                })?;
            insert_embedding(
                id,
                "content",
                embedding,
                dimensions,
                slot.index.as_ref(),
                storage.creators_engine(),
            )
            .map_err(|e| TidalError::internal("write_creator_embedding", format!("{e}")))?;
        }

        Ok(())
    }

    /// 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)),
        }
    }
}
