pub mod engine;
pub mod topic;

pub use engine::{EngineConfig, InMemoryQueueEngine};
pub use topic::{BackpressurePolicy, CompactResult, Topic, TopicStats};
// PublishResult and RemoveTopicResult are defined in this module and re-exported by default.

use std::future::Future;

use crate::error::Result;
use crate::types::{MessageEnvelope, NamespaceId, PublishedMessage, TopicName, TopicOffset};

/// Result of removing a topic from the engine.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RemoveTopicResult {
    /// Bytes freed from in-memory buffer.
    pub bytes_freed: u64,
    /// Messages freed from in-memory buffer.
    pub messages_freed: usize,
}

/// Result of a publish operation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PublishResult {
    /// The offset assigned to the published message.
    pub offset: TopicOffset,
    /// Number of messages evicted by DropOldest backpressure during this publish.
    pub evicted_messages: usize,
    /// Whether this publish created a new topic (first message to this topic).
    pub topic_created: bool,
}

/// Async interface to the indexed message log.
///
/// The queue engine stores messages and serves reads; it does NOT track
/// consumer cursors — that responsibility belongs to the namespace manager.
pub trait QueueEngine: Send + Sync {
    /// Publish a message to a topic.
    ///
    /// Auto-creates the topic on first publish. Returns a [`PublishResult`]
    /// containing the assigned offset and the number of messages evicted
    /// by DropOldest backpressure (0 for Reject/Block policies).
    fn publish(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
        msg: MessageEnvelope,
    ) -> impl Future<Output = Result<PublishResult>> + Send;

    /// Read the message at the given offset.
    ///
    /// Returns `Ok(None)` if the offset has been compacted away, is past the
    /// head, or the topic does not exist.
    fn consume_from(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
        offset: TopicOffset,
    ) -> impl Future<Output = Result<Option<PublishedMessage>>> + Send;

    /// Return the lowest retained offset in the in-memory buffer.
    ///
    /// Returns `TopicOffset::ZERO` for nonexistent topics.
    fn base_offset(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
    ) -> impl Future<Output = Result<TopicOffset>> + Send;

    /// Return the next offset to be assigned (one past the last written message).
    ///
    /// Returns `TopicOffset::ZERO` for nonexistent topics.
    fn head_offset(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
    ) -> impl Future<Output = Result<TopicOffset>> + Send;

    /// Remove all messages below `min_offset` from the in-memory buffer.
    ///
    /// No-op for nonexistent topics. Returns the compaction result.
    fn compact(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
        min_offset: TopicOffset,
    ) -> impl Future<Output = Result<CompactResult>> + Send;

    /// List all topic names in a namespace.
    fn list_topics(&self, ns: &NamespaceId) -> impl Future<Output = Vec<TopicName>> + Send;

    /// Read up to `max_count` messages starting from `start_offset` from
    /// persisted segments. Returns messages in offset order.
    fn read_cold_batch(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
        start_offset: TopicOffset,
        max_count: usize,
    ) -> impl Future<Output = Result<Vec<PublishedMessage>>> + Send;

    /// Find the offset of the first message at or after `timestamp_millis`.
    /// Delegates to the storage backend.
    fn offset_at_timestamp(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
        timestamp_millis: u64,
    ) -> impl Future<Output = Result<Option<TopicOffset>>> + Send;

    /// Return the total bytes on disk for a namespace's segments.
    fn namespace_disk_bytes(&self, ns: &NamespaceId) -> impl Future<Output = u64> + Send;

    /// Return a snapshot of the given topic's stats, or `None` if the topic
    /// does not exist.
    fn topic_stats(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
    ) -> impl Future<Output = Option<TopicStats>> + Send;

    /// Force-flush all pending writes to storage.
    ///
    /// Called before entering drain mode to ensure durability of all buffered
    /// data before clients disconnect.
    fn force_flush(&self) -> impl Future<Output = Result<()>> + Send;

    /// Publish a message at a specific pre-assigned offset (cluster replication).
    ///
    /// Used by followers to apply replicated entries at the leader-assigned offset.
    /// Default impl returns an error — only available when cluster feature is compiled
    /// and enabled.
    fn publish_with_offset(
        &self,
        _ns: &NamespaceId,
        _topic: &TopicName,
        _msg: MessageEnvelope,
        _offset: TopicOffset,
    ) -> impl Future<Output = Result<PublishResult>> + Send {
        async {
            Err(crate::error::Error::InvalidOperation(
                "publish_with_offset requires cluster feature".to_string(),
            ))
        }
    }

    /// Return the last activity timestamp (epoch millis) for a topic.
    /// Returns `None` if the topic does not exist.
    fn topic_last_activity(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
    ) -> impl Future<Output = Option<u64>> + Send;

    /// Remove a topic from the in-memory map. No-op if the topic does not exist.
    /// Updates global memory accounting. Does NOT touch storage — caller handles that.
    /// Returns a [`RemoveTopicResult`] with bytes and messages freed.
    fn remove_topic(
        &self,
        ns: &NamespaceId,
        topic: &TopicName,
    ) -> impl Future<Output = Result<RemoveTopicResult>> + Send;

    /// Flush storage and stop background tasks. Call before dropping.
    fn shutdown(&self) -> impl Future<Output = Result<()>> + Send;
}
