using LiteDB;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq.Expressions;

public class LiteDbHelper<T> : IDisposable where T : class
{
    private readonly LiteDatabase _database;
    private readonly ILiteCollection<T> _collection;

    /// <summary>
    /// Initializes a new instance of the LiteDbHelper class.
    /// </summary>
    /// <param name="collectionName">The name of the collection to work with.</param>
    public LiteDbHelper(string collectionName)
    {
        var dbPath = Nodify.Calculator.ProjectManager.DatabasePath;
        _database = new LiteDatabase(dbPath);
        _collection = _database.GetCollection<T>(collectionName);
    }

    // --- CRUD Operations ---

    /// <summary>
    /// Inserts a new document into the collection.
    /// </summary>
    /// <param name="document">The document to insert.</param>
    /// <returns>The BsonValue of the inserted document's Id.</returns>
    public BsonValue Insert(T document)
    {
        return _collection.Insert(document);
    }

    /// <summary>
    /// Inserts a collection of documents.
    /// </summary>
    /// <param name="documents">The documents to insert.</param>
    /// <returns>The number of documents inserted.</returns>
    public int BulkInsert(IEnumerable<T> documents)
    {
        return _collection.Insert(documents);
    }

    /// <summary>
    /// Updates a document in the collection.
    /// </summary>
    /// <param name="document">The document to update.</param>
    /// <returns>True if the document was updated, otherwise false.</returns>
    public bool Update(T document)
    {
        return _collection.Update(document);
    }

    /// <summary>
    /// Deletes a document from the collection by its Id.
    /// </summary>
    /// <param name="id">The Id of the document to delete.</param>
    /// <returns>True if the document was deleted, otherwise false.</returns>
    public bool Delete(BsonValue id)
    {
        return _collection.Delete(id);
    }

    /// <summary>
    /// Deletes documents from the collection based on a predicate.
    /// </summary>
    /// <param name="predicate">The expression to filter documents to delete.</param>
    /// <returns>The number of documents deleted.</returns>
    public int DeleteMany(Expression<Func<T, bool>> predicate)
    {
        return _collection.DeleteMany(predicate);
    }

    // --- Query Operations ---

    /// <summary>
    /// Finds a single document by its Id.
    /// </summary>
    /// <param name="id">The Id of the document.</param>
    /// <returns>The document if found, otherwise null.</returns>
    public T FindById(BsonValue id)
    {
        return _collection.FindById(id);
    }

    /// <summary>
    /// Finds the first document that matches the predicate.
    /// </summary>
    /// <param name="predicate">The expression to filter documents.</param>
    /// <returns>The first matching document, or null if none are found.</returns>
    public T FindOne(Expression<Func<T, bool>> predicate)
    {
        return _collection.FindOne(predicate);
    }

    /// <summary>
    /// Finds all documents in the collection.
    /// </summary>
    /// <returns>An enumerable of all documents.</returns>
    public IEnumerable<T> FindAll()
    {
        return _collection.FindAll();
    }

    /// <summary>
    /// Finds documents based on a predicate.
    /// </summary>
    /// <param name="predicate">The expression to filter documents.</param>
    /// <returns>An enumerable of matching documents.</returns>
    public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
    {
        return _collection.Find(predicate);
    }

    /// <summary>
    /// Checks if any document exists that matches the predicate.
    /// </summary>
    /// <param name="predicate">The expression to filter documents.</param>
    /// <returns>True if a matching document exists, otherwise false.</returns>
    public bool Exists(Expression<Func<T, bool>> predicate)
    {
        return _collection.Exists(predicate);
    }

    // --- Indexing ---

    /// <summary>
    /// Ensures that an index is created for the specified field.
    /// </summary>
    /// <param name="field">The expression representing the field to be indexed.</param>
    /// <param name="unique">Whether the index should enforce unique values.</param>
    /// <returns>True if the index was created, otherwise false.</returns>
    public bool EnsureIndex<K>(Expression<Func<T, K>> field, bool unique = false)
    {
        return _collection.EnsureIndex(field, unique);
    }

    // --- File Storage Operations ---

    /// <summary>
    /// Uploads a file to the LiteDB file storage.
    /// </summary>
    /// <param name="id">A unique identifier for the file.</param>
    /// <param name="filePath">The path to the file to upload.</param>
    public void UploadFile(string id, string filePath)
    {
        _database.FileStorage.Upload(id, filePath);
    }

    /// <summary>
    /// Downloads a file from the LiteDB file storage.
    /// </summary>
    /// <param name="id">The unique identifier of the file.</param>
    /// <param name="destinationPath">The path to save the downloaded file.</param>
    public void DownloadFile(string id, Stream destinationPath)
    {
        _database.FileStorage.Download(id, destinationPath);
    }

    /// <summary>
    /// Deletes a file from the LiteDB file storage.
    /// </summary>
    /// <param name="id">The unique identifier of the file to delete.</param>
    /// <returns>True if the file was deleted, otherwise false.</returns>
    public bool DeleteFile(string id)
    {
        return _database.FileStorage.Delete(id);
    }

    /// <summary>
    /// Finds a file's metadata in the LiteDB file storage.
    /// </summary>
    /// <param name="id">The unique identifier of the file.</param>
    /// <returns>The LiteFileInfo object if found, otherwise null.</returns>
    public LiteFileInfo<string> FindFileById(string id)
    {
        return _database.FileStorage.FindById(id);
    }

    /// <summary>
    /// Disposes the LiteDatabase connection.
    /// </summary>
    public void Dispose()
    {
        _database?.Dispose();
    }
}