using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.MongoModels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoUtils = adas_core.Infrastructure.Utils.MongoUtils;

namespace adas_core.Infrastructure.Repositories;

/// <summary>
/// Repository implementation for managing HistoricalConfigChanges entities in MongoDB.
/// Provides CRUD operations and specialized queries for tracking configuration changes over time.
/// </summary>
public class HistoricalConfigChangesRepository : MongoRepository<HistoricalConfigChanges>,
    IHistoricalConfigChangesRepository
{
    private readonly ApiSettings _apiSettings;
    private readonly ILogger<HistoricalConfigChangesRepository> _logger;

    /// <summary>
    /// Initializes a new instance of the HistoricalConfigChangesRepository.
    /// </summary>
    /// <param name="apiSettings">API settings containing collection names configuration.</param>
    /// <param name="database">The MongoDB database instance.</param>
    /// <param name="logger">Logger for repository operations.</param>
    /// <exception cref="ArgumentNullException">Thrown when apiSettings is null.</exception>
    public HistoricalConfigChangesRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database,
        ILogger<HistoricalConfigChangesRepository> logger) : base(database)
    {
        if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
        _apiSettings = apiSettings.Value;
        _logger = logger;
    }

    /// <summary>
    /// Gets the name of the collection for historical configuration changes.
    /// </summary>
    /// <returns>The collection name from API settings, or default "historicalConfigChanges".</returns>
    public override string GetCollectionName()
    {
        return _apiSettings.HistoricalConfigChanges ?? "historicalConfigChanges";
    }

    /// <summary>
    /// Inserts a new historical configuration change record and returns the inserted document.
    /// </summary>
    /// <param name="historicalConfigChanges">The HistoricalConfigChanges entity to insert.</param>
    /// <returns>The inserted HistoricalConfigChanges with generated ID, or null if insertion fails.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    public override async Task<HistoricalConfigChanges?> InsertOneAsync(HistoricalConfigChanges historicalConfigChanges)
    {
        try
        {
            await Collection.InsertOneAsync(historicalConfigChanges);
            return await FindById(historicalConfigChanges.Id);
        }
        catch (Exception ex)
        {
            _logger.LogError("Error inserting historicalConfigChanges {exMessage}", ex.Message);
            return null;
        }
    }

    /// <summary>
    /// Deletes a historical configuration change record by its ID.
    /// </summary>
    /// <param name="id">The ObjectId of the record to delete.</param>
    /// <returns>The deleted HistoricalConfigChanges if found; otherwise, null.</returns>
    public async Task<HistoricalConfigChanges?> Delete(ObjectId id)
    {
        return await DeleteAsync(id);
    }

    /// <summary>
    /// Retrieves all historical configuration change records.
    /// </summary>
    /// <returns>A collection of all HistoricalConfigChanges entities.</returns>
    public async Task<ICollection<HistoricalConfigChanges>> FindAll()
    {
        var result = await Collection.FindAsync(_ => true);
        return await result.ToListAsync();
    }

    /// <summary>
    /// Retrieves all IDs of historical configuration change records.
    /// </summary>
    /// <returns>A list of all ObjectIds in the collection.</returns>
    public async Task<List<ObjectId>> FindAllIds()
    {
        List<ObjectId> listCollection = [];
        var allCollection = await Collection.FindAsync(_ => true);

        listCollection.AddRange(allCollection.ToList().Select(item => item.Id));

        return listCollection;
    }

    /// <summary>
    /// Retrieves a historical configuration change record by its ID.
    /// </summary>
    /// <param name="id">The ObjectId of the record to retrieve.</param>
    /// <returns>The HistoricalConfigChanges if found; otherwise, null.</returns>
    public async Task<HistoricalConfigChanges?> FindById(ObjectId id)
    {
        var result = await Collection.FindAsync(Builders<HistoricalConfigChanges>.Filter.Eq(x => x.Id, id));
        return await result.FirstOrDefaultAsync();
    }

    /// <summary>
    /// Updates an existing historical configuration change record.
    /// </summary>
    /// <param name="historicalConfigChanges">The HistoricalConfigChanges with updated values.</param>
    /// <returns>The updated HistoricalConfigChanges if successful; otherwise, null.</returns>
    public async Task<HistoricalConfigChanges?> Update(HistoricalConfigChanges historicalConfigChanges)
    {
        var filter = Builders<HistoricalConfigChanges>.Filter.Eq("_id", historicalConfigChanges.Id);
        var update = Builders<HistoricalConfigChanges>.Update
            .Set(c => c, historicalConfigChanges);

        return await Collection.FindOneAndUpdateAsync(
            filter,
            update,
            new FindOneAndUpdateOptions<HistoricalConfigChanges, HistoricalConfigChanges>
            {
                ReturnDocument = ReturnDocument.After
            });
    }

    /// <summary>
    /// Retrieves the most recent historical configuration changes for a specific configuration type.
    /// Results are sorted by time in descending order.
    /// </summary>
    /// <param name="cfgType">The type of configuration to filter by.</param>
    /// <param name="num">The maximum number of records to retrieve. Default is 10.</param>
    /// <returns>A collection of the most recent HistoricalConfigChanges for the specified type.</returns>
    public async Task<ICollection<HistoricalConfigChanges>> FindLastHistoricalConfigChangesByType(
        DisplayConfigEnums.ConfigTypes cfgType, int num = 10)
    {
        var filterDefinitionBuilder = Builders<HistoricalConfigChanges>.Filter;
        var filter = filterDefinitionBuilder.Eq(c => c.ConfigType, cfgType);

        var result = await Collection.FindAsync(
            filter,
            new FindOptions<HistoricalConfigChanges>
            { Sort = Builders<HistoricalConfigChanges>.Sort.Descending("time"), Limit = num }
        );

        return await result.ToListAsync();
    }

    /// <summary>
    /// Retrieves the most recent historical configuration changes for a specific user.
    /// Optionally filters by configuration type. Results are sorted by time in descending order.
    /// </summary>
    /// <param name="user">The username to filter by.</param>
    /// <param name="cfgType">Optional configuration type to filter by. If null, all types are included.</param>
    /// <param name="num">The maximum number of records to retrieve. Default is 10.</param>
    /// <returns>A collection of the most recent HistoricalConfigChanges for the specified user.</returns>
    public async Task<ICollection<HistoricalConfigChanges>> FindLastHistoricalConfigChangesByUser(string user,
        DisplayConfigEnums.ConfigTypes? cfgType = null, int num = 10)
    {
        var filterDefinitionBuilder = Builders<HistoricalConfigChanges>.Filter;
        var filter = filterDefinitionBuilder.Eq(c => c.Username, user);

        if (cfgType != null) filter &= filterDefinitionBuilder.Eq(c => c.ConfigType, cfgType.Value);

        var result = await Collection.FindAsync(
            filter,
            new FindOptions<HistoricalConfigChanges>
            { Sort = Builders<HistoricalConfigChanges>.Sort.Descending("time"), Limit = num }
        );

        return await result.ToListAsync();
    }

    /// <summary>
    /// Creates the necessary indexes for the HistoricalConfigChanges collection.
    /// Creates compound indexes on (configType, time) and (username, time) for improved query performance.
    /// </summary>
    /// <exception cref="Exception">Throws an exception if index creation fails; logs error details before throwing.</exception>
    public override async Task CreateIndexes()
    {
        try
        {
            var options = new CreateIndexOptions { Background = true, Unique = false };
            var indexes = new List<CreateIndexModel<HistoricalConfigChanges>>
            {
                new("{ configType: 1, time:-1 }", options),
                new("{ username: 1, time: -1 }", options)
            };
            await MongoUtils.EnsureIndexes(Collection, indexes);
        }
        catch (Exception e)
        {
            _logger.LogError(
                "error creating indexes for HistoricalConfigChanges collection {eMessage} TRACE: {eStackTrace}",
                e.Message, e.StackTrace);
            throw;
        }
    }
}