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

namespace adas_core.Infrastructure.Repositories;

public class NoticeRepository : MongoRepository<Notice>, INoticeRepository
{
    private readonly ApiSettings _apiSettings;

    public NoticeRepository(IOptions<ApiSettings>? apiSettings, IMongoDatabase database) : base(database)
    {
        if (apiSettings != null)
            _apiSettings = apiSettings.Value;
        else
            throw new ArgumentNullException(nameof(apiSettings));
    }

    

    public override string GetCollectionName()
    {
        return _apiSettings.Notices;
    }

    public override async Task InsertOneAsync(Notice notice)
    {
        try
        {
            notice.NoticeDate = DateTime.UtcNow;
            await Collection.InsertOneAsync(notice);
        }
        catch (Exception e)
        {
            Log.Warning("Exception trying to insert  notice: {notice}. Exception {e}", notice, e);
        }
    }

    public async Task Delete(ObjectId id)
    {
        try
        {
            var filter = Builders<Notice>.Filter.Eq(x => x.Id, id);
            await Collection.DeleteOneAsync(filter, null);
        }
        catch (Exception e)
        {
            Log.Error("Exception trying to delete notice: {id}. Exception {e}", id, e);
            throw;
        }
    }

    public async Task Update(Notice notice)
    {
        try
        {
            await UpdateOneAsync(notice.Id, notice);
        }
        catch (Exception e)
        {
            Log.Error("Exception trying to update notice: {notice}. Exception {e}", notice, e);
            throw;
        }
    }

    public async Task<IEnumerable<Notice>> FindAll()
    {
        try
        {
            var result = await Collection.FindAsync(_ => true);
            return await result.ToListAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error getting all notices. Exception: {ex}", ex);
            return [];
        }
    }

    public async Task<Notice?> FindById(ObjectId id)
    {
        try
        {
            var filter = Builders<Notice>.Filter.Eq(p => p.Id, id);
            var result = await Collection.FindAsync(filter);

            return await result.FirstOrDefaultAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching notice by id: {id}. Exception: {ex}", id, ex);
            return null;
        }
    }

    public async Task<IEnumerable<Notice>?> FindByDate(DateTime date)
    {
        try
        {
            var filter = Builders<Notice>.Filter.Eq(p => p.NoticeDate, date);
            var result = await Collection.FindAsync(filter);

            return result.ToEnumerable();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching notice by date: {date}. Exception: {ex}", date, ex);
            return null;
        }
    }

    public async Task<IEnumerable<Notice>?> FindByType(string type)
    {
        try
        {
            var filter = Builders<Notice>.Filter.Eq(p => p.NoticeType, type);
            var result = await Collection.FindAsync(filter);

            return result.ToEnumerable();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching notice by type: {date}. Exception: {ex}", type, ex);
            return null;
        }
    }

    public async Task<IEnumerable<Notice>?> FindByDisplayId(ObjectId displayId)
    {
        try
        {
            var filter = Builders<Notice>.Filter.Eq(p => p.DisplayId, displayId);
            var result = await Collection.FindAsync(filter);

            return result.ToEnumerable();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching notices by display id: {id}. Exception: {ex}", displayId, ex);
            return null;
        }
    }

    public override async Task CreateIndexes()
    {
        var options = new CreateIndexOptions { Background = true, Unique = false };

        var indexes = new List<CreateIndexModel<Notice>>
        {
            new("{ noticeType: 1, noticeDate: -1 }", options),
            new("{ noticeDate: -1 }", options)
        };

        await MongoUtils.EnsureIndexes(Collection, indexes);
    }
}