using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;

namespace adas_core.Infrastructure.Repositories;

/// <summary>
/// Repository for managing display card configurations in MongoDB. Provides methods to perform CRUD operations on CardConfig documents.
/// </summary>
public class DisplayCardConfigRepository : MongoRepository<CardConfig>, IDisplayCardConfigRepository
{
    private readonly ApiSettings _apiSettings;
    private readonly ILogger<DisplayCardConfigRepository> _logger;

    /// <summary>
    /// Initializes a new instance of the DisplayCardConfigRepository class with the specified MongoDB database, API settings, and logger.
    /// </summary>
    /// <param name="database">The MongoDB database instance.</param>
    /// <param name="apiSettings">The API settings containing configuration for the repository.</param>
    /// <param name="logger">The logger instance for logging repository operations.</param>
    public DisplayCardConfigRepository(
        IMongoDatabase database,
        ApiSettings apiSettings,
        ILogger<DisplayCardConfigRepository> logger
    ) : base(database)
    {
        _apiSettings = apiSettings;
        _logger = logger;
    }

    /// <summary>
    /// Gets the name of the MongoDB collection for CardConfig documents, as specified in the API settings.
    /// </summary>
    /// <returns>The name of the MongoDB collection for CardConfig documents.</returns>
    public override string GetCollectionName()
    {
        return _apiSettings.DisplayCardConfig;
    }

    /// <summary>
    /// Retrieves all CardConfig documents from the MongoDB collection and returns them as a list. Logs any exceptions that occur during the retrieval process.
    /// </summary>
    /// <returns>A task representing the asynchronous operation, containing a list of all CardConfig documents.</returns>
    public async Task<List<CardConfig>> GetAll()
    {
        var result = await Collection.Find(Builders<CardConfig>.Filter.Empty).ToListAsync();
        return result;
    }

    /// <summary>
    /// Retrieves a CardConfig document by its unique identifier from the MongoDB collection. Logs any exceptions that occur during the retrieval process and returns null if an error occurs or if the document is not found.
    /// </summary>
    /// <param name="configId">The unique identifier of the CardConfig document.</param>
    /// <returns>A task representing the asynchronous operation, containing the CardConfig document if found, or null if not found or an error occurs.</returns>
    public async Task<CardConfig?> GetById(ObjectId configId)
    {
        try
        {
            var result = await Collection.FindAsync(Builders<CardConfig>.Filter.Eq(p => p.Id, configId));
            return await result.FirstOrDefaultAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError("Error on config card display repository on GetById Exception: {ex}", ex.Message);
            return null;
        }
    }

    /// <summary>
    /// Inserts a new CardConfig document into the MongoDB collection and returns the inserted document. Logs any exceptions that occur during the insertion process and returns null if an error occurs.
    /// </summary>
    /// <param name="config">The CardConfig document to insert.</param>
    /// <returns>A task representing the asynchronous operation, containing the inserted CardConfig document if successful, or null if an error occurs.</returns>
    public async Task<CardConfig?> InsertOneAsyncAndReturn(CardConfig config)
    {
        try
        {
            await Collection.InsertOneAsync(config);
            return config;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex.Message);
            return null;
        }
    }

    /// <summary>
    /// Updates an existing CardConfig document in the MongoDB collection based on the provided CardConfig object. 
    /// The method updates the Rows field of the document with the matching Id.
    /// It returns an UpdateResponse containing the number of modified documents and the updated document itself. 
    /// Logs any exceptions that occur during the update process and returns an UpdateResponse with zero changes and null data if an error occurs or if the input config is null.
    /// </summary>
    /// <param name="config">The CardConfig document to update.</param>
    /// <returns>A task representing the asynchronous operation, containing an UpdateResponse with the number of modified documents and the updated document.</returns>
    public async Task<UpdateResponse<CardConfig?>> UpdateOne(CardConfig? config)
    {
        try
        {
            if (config == null) return new UpdateResponse<CardConfig?>(0, null);
            var filter = Builders<CardConfig>.Filter.Eq(c => c.Id, config.Id);
            var update = Builders<CardConfig>.Update.Set(c => c.Rows, config.Rows);

            var result = await Collection.UpdateOneAsync(filter, update);

            var updatedDoc = await Collection.Find(filter).FirstOrDefaultAsync();

            return new UpdateResponse<CardConfig?>(result.ModifiedCount, updatedDoc);
        }
        catch (Exception e)
        {
            _logger.LogError(e.Message);
            return new UpdateResponse<CardConfig?>(0, null);
        }
    }

    /// <summary>
    /// Deletes a CardConfig document from the MongoDB collection based on the provided unique identifier.
    /// </summary>
    /// <param name="configId">The unique identifier of the CardConfig document to delete.</param>
    /// <returns>A task representing the asynchronous operation, containing the deleted CardConfig document if successful, or null if not found or an error occurs.</returns>
    public async Task<CardConfig?> DeleteOne(ObjectId configId)
    {
        return await DeleteAsync(configId);
    }

    /// <summary>
    /// Updates the CardConfig document associated with the specified display configuration ID and result ID.
    /// </summary>
    /// <param name="displayConfigId">The unique identifier of the display configuration.</param>
    /// <param name="resultId">The unique identifier of the result.</param>
    /// <returns>A task representing the asynchronous operation, containing the updated CardConfig document if successful, or null if not found or an error occurs. </returns>
    /// <exception cref="NotImplementedException"></exception>
    public Task<object> UpdateCardConfigId(ObjectId? displayConfigId, ObjectId? resultId)
    {
        throw new NotImplementedException();
    }
}