using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.Filter;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using System.Text.RegularExpressions;

namespace adas_core.Infrastructure.Repositories;

public class MedicineRepository : MongoRepository<Medicine>, IMedicineRepository
{
    private readonly ApiSettings _apiSettings;

    public MedicineRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database) : base(database)
    {
        if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
        _apiSettings = apiSettings.Value;
    } //For testing

    

    public override string GetCollectionName()
    {
        return _apiSettings.Medicines ?? "medicines";
    }

    public async Task<Medicine?> GetMedicine(string code)
    {
        var result = await Collection.FindAsync(x => x.Codes.Contains(code) || x.Notes.Contains(code));

        return await result.FirstOrDefaultAsync();
    }


    public async Task<List<Medicine>> GetMedicineByCodeOrNote(List<string> codeNotes)
    {
        var filter = Builders<Medicine>.Filter.AnyIn("Codes", codeNotes.ToArray());
        var result = await Collection.FindAsync(filter);

        return result.ToList();
    }

    public async Task<Medicine?> GetMedicineByName(string name)
    {
        var filter = Builders<Medicine>.Filter.Eq(p => p.Name, name);
        var result = await Collection.FindAsync(filter);

        return await result.FirstOrDefaultAsync();
    }

    public async Task<List<Medicine>> GetAll()
    {
        var result = await Collection.FindAsync(_ => true);

        return result.ToList();
    }

    public async Task<Medicine?> GetMedicineById(ObjectId medicineId)
    {
        var filter = Builders<Medicine>.Filter.Eq(p => p.Id, medicineId);
        var result = await Collection.FindAsync(filter);

        return await result.FirstOrDefaultAsync();
    }

    public async Task<Medicine?> PostMedicine(Medicine medicine)
    {
        await Collection.InsertOneAsync(medicine);
        var result = await Collection.FindAsync(v => v.Name == medicine.Name);

        return await result.FirstOrDefaultAsync();
    }

    public async Task<Medicine?> UpdateMedicine(Medicine medicine)
    {
        await UpdateOneAsync(medicine.Id, medicine);

        return medicine;
    }

    public async Task DeleteMedicineById(ObjectId medicineId)
    {
        var filter = Builders<Medicine>.Filter.Eq(po => po.Id, medicineId);

        await Collection.DeleteOneAsync(filter);
    }

    public IFindFluent<Medicine, Medicine> GetPaginatedMedicines(PaginationFilter filter)
    {
        // Crear variable con la clase que construye los filtros que necesitamos
        var filterBuilder = Builders<Medicine>.Filter;
        // Crear una lista de filtros que pueden venir de tu servicio
        var filters = new List<FilterDefinition<Medicine>>();
        // Ordenar los resultados por "time" en orden descendente
        var sort = Builders<Medicine>.Sort.Ascending("name");
        if (filter.FilteredRequest != null)
        {
            var requestFilter = filter.FilteredRequest;

            if (!string.IsNullOrWhiteSpace(requestFilter.MedicineName))
            {
                var escapedTextFilter = Regex.Escape(requestFilter.MedicineName);
                filters.Add(filterBuilder.Regex(m => m.Name,
                    new BsonRegularExpression(escapedTextFilter, "i")));
            }
            if (!string.IsNullOrWhiteSpace(requestFilter.MedicineCode))
                filters.Add(filterBuilder.AnyEq(m => m.Codes, requestFilter.MedicineCode));

            if (!string.IsNullOrWhiteSpace(requestFilter.MedicineType))
                filters.Add(filterBuilder.AnyEq(m => m.Type, requestFilter.MedicineType));

            if (!string.IsNullOrWhiteSpace(requestFilter.MedicineGroup))
                filters.Add(filterBuilder.AnyEq(m => m.Group, requestFilter.MedicineGroup));
        }

        if (filters.Count == 0)
            return Collection.Find(_ => true).Sort(sort);

        var combinedFilter = Builders<Medicine>.Filter.And(filters);

        return Collection
            .Find(combinedFilter)
            .Sort(sort);
    }

    public IAggregateFluent<BsonDocument> GetDistinctFieldDataQuery(string field)
    {
        return Collection.Aggregate()
            .Unwind(field)
            .Group(new BsonDocument { { "_id", $"${field}" } })
            .Sort(new BsonDocument { { "_id", 1 } })
            .Project(new BsonDocument { { field, "$_id" }, { "_id", 0 } });
    }


    public Task<List<string>> GetAllGroups()
    {
        throw new NotImplementedException();
    }
}