//! MongoDB connection wrapper with typed collection accessors.

use mongodb::{bson::doc, Client, Collection};

use super::Error;
use crate::models::{ChatMessage, ChatSession, OrgBillingRecord, OrgSettings, UserPreferences};

/// Thin wrapper around [`mongodb::Database`] that provides typed
/// collection accessors for the application's domain models.
#[derive(Clone, Debug)]
pub struct Database {
    inner: mongodb::Database,
}

impl Database {
    /// Connect to MongoDB, select the given database, and verify
    /// connectivity with a `ping` command.
    ///
    /// # Arguments
    ///
    /// * `uri` - MongoDB connection string (e.g. `mongodb://localhost:27017`)
    /// * `db_name` - Database name to use
    ///
    /// # Errors
    ///
    /// Returns `Error::DatabaseError` if the client cannot be created
    /// or the ping fails.
    pub async fn connect(uri: &str, db_name: &str) -> Result<Self, Error> {
        let client = Client::with_uri_str(uri).await?;
        let db = client.database(db_name);

        // Verify the connection is alive.
        db.run_command(doc! { "ping": 1 }).await?;

        Ok(Self { inner: db })
    }

    /// Collection for per-user preferences (theme, custom topics, etc.).
    pub fn user_preferences(&self) -> Collection<UserPreferences> {
        self.inner.collection("user_preferences")
    }

    /// Collection for organisation-level settings.
    pub fn org_settings(&self) -> Collection<OrgSettings> {
        self.inner.collection("org_settings")
    }

    /// Collection for per-cycle billing records.
    pub fn org_billing(&self) -> Collection<OrgBillingRecord> {
        self.inner.collection("org_billing")
    }

    /// Collection for persisted chat sessions (sidebar listing).
    pub fn chat_sessions(&self) -> Collection<ChatSession> {
        self.inner.collection("chat_sessions")
    }

    /// Collection for individual chat messages within sessions.
    pub fn chat_messages(&self) -> Collection<ChatMessage> {
        self.inner.collection("chat_messages")
    }

    /// Raw BSON document collection for queries that need manual
    /// `_id` → `String` conversion (avoids `ObjectId` deserialization issues).
    pub fn raw_collection(&self, name: &str) -> Collection<mongodb::bson::Document> {
        self.inner.collection(name)
    }
}
