using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.MongoModels;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
using Serilog;
using System.Text.RegularExpressions;

namespace adas_core.Infrastructure.Repositories;

public class UnitRepository : MongoRepository<Unit>, IUnitRepository
{
    #region Properties

    private readonly ApiSettings _apiSettings;

    #endregion

    #region Constructor

    

    public UnitRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database) : base(database)
    {
        _apiSettings = apiSettings.Value;
    }

    #endregion

    #region Methods

    #region Create

    public async Task<Unit?> InsertOneUnit(Unit unit)
    {
        try
        {
            await Collection.InsertOneAsync(unit);
            return await FindById(unit.Id);
        }
        catch (Exception ex)
        {
            Log.Error("Error inserting Unit: {unit}. Exception: {ex}",
                JsonConvert.SerializeObject(unit, Formatting.Indented), ex);

            return null;
        }
    }

    #endregion

    #region Read

    public override string GetCollectionName()
    {
        return _apiSettings.Units ?? "units";
    }

    // public async Task<Unit?> FindByLocation(PatientLocation location)
    // {
    //     var filter = Builders<Unit>.Filter.ElemMatch(x => x.PointOfCares, poc => poc.Bed == location.Bed && poc.UnitName == location.UnitName);
    //     var result = await Collection.Find(filter).FirstOrDefaultAsync();
    //
    //     return result; 
    // }

    public async Task<Unit?> FindById(object id)
    {
        var result = await Collection.FindAsync(Builders<Unit>.Filter.Eq(x => x.Id, id));
        return await result.FirstOrDefaultAsync();
    }

    public Task<Unit?> FindById(string id)
    {
        throw new NotImplementedException();
    }

    public async Task<IEnumerable<Unit>> FindByMasterListId(ObjectId id, MasterListType masterListType)
    {
        try
        {
            var propertyName = $"{masterListType}Id"; // nombre de la propiedad dinámicamente
            var filter = Builders<Unit>.Filter.Eq(propertyName, id);


            var result = await Collection.Find(filter).ToListAsync();

            return result;
        }
        catch (Exception ex)
        {
            Log.Error("Error searching unit by {masterlisttype} Id {id}. Exception: {ex}", masterListType.ToString(),
                id, ex);

            throw;
        }
    }

    public async Task<IEnumerable<Unit>> FindByMasterListId(ObjectId id)
    {
        try
        {
            var filterBuilder = Builders<Unit>.Filter;
            var filters = new List<FilterDefinition<Unit>>
            {
                filterBuilder.Or(
                    filterBuilder.Eq(p => p.DoctorListId, id),
                    filterBuilder.Eq(p => p.AllergyListId, id),
                    filterBuilder.Eq(p => p.DestinationListId, id),
                    filterBuilder.Eq(p => p.DiagnosisListId, id),
                    filterBuilder.Eq(p => p.InsulationListId, id),
                    filterBuilder.Eq(p => p.OriginListId, id),
                    filterBuilder.Eq(p => p.ProcedureListId, id),
                    filterBuilder.Eq(p => p.TestListId, id),
                    filterBuilder.Eq(p => p.ServiceListId, id),
                    filterBuilder.Eq(p => p.TreatmentListId, id),
                    filterBuilder.Eq(p => p.LanguageBarrierListId, id),
                    filterBuilder.Eq(p => p.AltableOptionListId, id),
                    filterBuilder.Eq(p => p.DischargeStatusListId, id),
                    filterBuilder.Eq(p => p.DoctorTypeListId, id),
                    filterBuilder.Eq(p => p.InternalDestinationListId, id),
                    filterBuilder.Eq(p => p.PassiveSittingListId, id),
                    filterBuilder.Eq(p => p.GenericListId, id),
                    filterBuilder.Eq(p => p.VisitOptionListId, id),
                    filterBuilder.Eq(p => p.AccessControlListId, id),
                    filterBuilder.Eq(p => p.TherapeuticCeilingListId, id),
                    filterBuilder.Eq(p => p.MobilityOptionListId, id)
                )
            };


            var result = await Collection.Find(Builders<Unit>.Filter.And(filters)).ToListAsync();

            return result;
        }
        catch (Exception ex)
        {
            Log.Error("Error searching unit by masterlist Id {id}. Exception: {ex}", id.ToString(), ex);

            return new List<Unit>();
        }
    }

    public async Task<long> CountUnitsByMasterListId(ObjectId id, MasterListType masterListType)
    {
        var propertyName = $"{masterListType}Id";
        var filter = Builders<Unit>.Filter.Eq(propertyName, id);
        var result = await Collection.CountDocumentsAsync(filter);
        return result;
    }

    public async Task<Unit?> FindByName(string unitName)
    {
        var result = await Collection.FindAsync(Builders<Unit>.Filter.Eq(x => x.Name, unitName));

        return await result.FirstOrDefaultAsync();
    }

    // public async Task<List<Unit>> FindByPointOfCare(PointOfCare pointOfCare)
    // {
    //     var filter = Builders<Unit>.Filter.ElemMatch(x => x.PointOfCares, poc => poc.Bed == pointOfCare.Bed && poc.Room == pointOfCare.Room && pointOfCare.unitName == poc.unitName);
    //     var result = await Collection.Find(filter).ToListAsync();
    //
    //     return result;    
    // }
    // public async Task<Unit?> FindByPointOfCare(PointOfCare pointOfCare)
    // {
    //     var filter = Builders<Unit>.Filter.ElemMatch(x => x.PointOfCares, poc => poc.Bed == pointOfCare.Bed && poc.Room == pointOfCare.Room && pointOfCare.UnitName == poc.UnitName);
    //     var result = await Collection.Find(filter).FirstOrDefaultAsync();
    //
    //     return result;    
    // }
    // public async Task<List<Unit>> FindByUnitName(string unitName)
    // {
    //     var filter = Builders<Unit>.Filter.ElemMatch(x => x.PointOfCares, poc => poc.UnitName == unitName);
    //     var result = await Collection.Find(filter).ToListAsync();
    //
    //     return result;    
    // }
    public async Task<List<Unit>> GetAll()
    {
        var result = await Collection.FindAsync(Builders<Unit>.Filter.Empty);

        return result.ToList();
    }


    public IFindFluent<Unit, Unit> GetPaginatedUnits(PaginationFilter filter)
    {
        var filterBuilder = Builders<Unit>.Filter;
        var sort = Builders<Unit>.Sort.Ascending("title");
        var filters = new List<FilterDefinition<Unit>>();

        if (filter.FilteredRequest == null) return CreateFindFluent(filters, sort);

        if (!string.IsNullOrEmpty(filter.FilteredRequest?.Text))
        {
            var textFilter = filter.FilteredRequest.Text;
            var textFilterEscaped = Regex.Escape(textFilter);

            filters.Add(
                filterBuilder.Or(
                    filterBuilder.Regex(p => p.Name,
                        new BsonRegularExpression(textFilterEscaped, "i")), // Case-insensitive regex match for name
                    filterBuilder.Regex(p => p.Title,
                        new BsonRegularExpression(textFilterEscaped, "i")) // Case-insensitive regex match for title
                )
            );
        }

        return CreateFindFluent(filters, sort);
    }

    private IFindFluent<Unit, Unit> CreateFindFluent(List<FilterDefinition<Unit>> filters, SortDefinition<Unit> sort)
    {
        var combinedFilter = filters.Any()
            ? Builders<Unit>.Filter.And(filters)
            : Builders<Unit>.Filter.Empty;
        return Collection.Find(combinedFilter).Sort(sort);
    }

    #endregion

    #region Update

    public async Task<Unit?> UpdateUnit(Unit unit)
    {
        var filter = Builders<Unit>.Filter.Eq("_id", unit.Id);
        var update = Builders<Unit>.Update
            //.Set(c => c.Id, unit.Id)
            .Set(c => c.Title, unit.Title)
            .Set(c => c.Name, unit.Name)
            .Set(c => c.Configuration, unit.Configuration)
            .Set(c => c.AllergyListId, unit.AllergyListId)
            .Set(c => c.DestinationListId, unit.DestinationListId)
            .Set(c => c.InternalDestinationListId, unit.InternalDestinationListId)
            .Set(c => c.DiagnosisListId, unit.DiagnosisListId)
            .Set(c => c.DoctorListId, unit.DoctorListId)
            .Set(c => c.DoctorTypeListId, unit.DoctorTypeListId)
            .Set(c => c.InsulationListId, unit.InsulationListId)
            .Set(c => c.MobilityOptionListId, unit.MobilityOptionListId)
            .Set(c => c.OriginListId, unit.OriginListId)
            .Set(c => c.PatientStatusListId, unit.PatientStatusListId)
            .Set(c => c.ProcedureListId, unit.ProcedureListId)
            .Set(c => c.TestListId, unit.TestListId)
            .Set(c => c.ServiceListId, unit.ServiceListId)
            .Set(c => c.TherapeuticCeilingListId, unit.TherapeuticCeilingListId)
            .Set(c => c.TreatmentListId, unit.TreatmentListId)
            .Set(c => c.VisitOptionListId, unit.VisitOptionListId)
            .Set(c => c.AccessControlListId, unit.AccessControlListId)
            .Set(c => c.DischargeStatusListId, unit.DischargeStatusListId);

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

    public async Task<Unit?> UpdateUnitInfo(ObjectId unitId, string name, string title)
    {
        var filter = Builders<Unit>.Filter.Eq("_id", unitId);
        var update = Builders<Unit>.Update
            //.Set(c => c.Id, unit.Id)
            .Set(c => c.Title, title)
            .Set(c => c.Name, name);


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

    public async Task<Unit?> UpdateUnitMasterList(UpdateUnitIdListDto updateUnitListDto)
    {
        var filter = Builders<Unit>.Filter.Eq("_id", updateUnitListDto.UnitId);
        var update = Builders<Unit>.Update;
        var updates = new List<UpdateDefinition<Unit>>();

        foreach (var masterListData in updateUnitListDto.MasterListData)
            if (masterListData.MasterListType.HasValue)
                switch (masterListData.MasterListType.Value)
                {
                    case MasterListType.AltableOptionList:
                        updates.Add(update.Set(u => u.AltableOptionListId, masterListData.MasterListId));
                        break;
                    case MasterListType.AllergyList:
                        updates.Add(update.Set(u => u.AllergyListId, masterListData.MasterListId));
                        break;
                    case MasterListType.DestinationList:
                        updates.Add(update.Set(u => u.DestinationListId, masterListData.MasterListId));
                        break;
                    case MasterListType.DiagnosisList:
                        updates.Add(update.Set(u => u.DiagnosisListId, masterListData.MasterListId));
                        break;
                    case MasterListType.DischargeStatusList:
                        updates.Add(update.Set(u => u.DischargeStatusListId, masterListData.MasterListId));
                        break;
                    case MasterListType.DoctorList:
                        updates.Add(update.Set(u => u.DoctorListId, masterListData.MasterListId));
                        break;
                    case MasterListType.DoctorTypeList:
                        updates.Add(update.Set(u => u.DoctorTypeListId, masterListData.MasterListId));
                        break;
                    case MasterListType.InternalDestinationList:
                        updates.Add(update.Set(u => u.InternalDestinationListId, masterListData.MasterListId));
                        break;
                    case MasterListType.InsulationList:
                        updates.Add(update.Set(u => u.InsulationListId, masterListData.MasterListId));
                        break;
                    case MasterListType.LanguageBarrierList:
                        updates.Add(update.Set(u => u.LanguageBarrierListId, masterListData.MasterListId));
                        break;
                    case MasterListType.PassiveSittingList:
                        updates.Add(update.Set(u => u.PassiveSittingListId, masterListData.MasterListId));
                        break;
                    case MasterListType.GenericList:
                        updates.Add(update.Set(u => u.GenericListId, masterListData.MasterListId));
                        break;
                    case MasterListType.MobilityOptionList:
                        updates.Add(update.Set(u => u.MobilityOptionListId, masterListData.MasterListId));
                        break;
                    case MasterListType.OriginList:
                        updates.Add(update.Set(u => u.OriginListId, masterListData.MasterListId));
                        break;
                    case MasterListType.PatientStatusList:
                        updates.Add(update.Set(u => u.PatientStatusListId, masterListData.MasterListId));
                        break;
                    case MasterListType.ProcedureList:
                        updates.Add(update.Set(u => u.ProcedureListId, masterListData.MasterListId));
                        break;
                    case MasterListType.TestList:
                        updates.Add(update.Set(u => u.TestListId, masterListData.MasterListId));
                        break;
                    case MasterListType.ServiceList:
                        updates.Add(update.Set(u => u.ServiceListId, masterListData.MasterListId));
                        break;
                    case MasterListType.TherapeuticCeilingList:
                        updates.Add(update.Set(u => u.TherapeuticCeilingListId, masterListData.MasterListId));
                        break;
                    case MasterListType.TreatmentList:
                        updates.Add(update.Set(u => u.TreatmentListId, masterListData.MasterListId));
                        break;
                    case MasterListType.VisitOptionList:
                        updates.Add(update.Set(u => u.VisitOptionListId, masterListData.MasterListId));
                        break;
                    case MasterListType.AccessControlList:
                        updates.Add(update.Set(u => u.AccessControlListId, masterListData.MasterListId));
                        break;
                }

        if (updates.Any())
        {
            var combinedUpdate = update.Combine(updates);
            return await Collection.FindOneAndUpdateAsync(filter, combinedUpdate,
                new FindOneAndUpdateOptions<Unit, Unit> { ReturnDocument = ReturnDocument.After });
        }

        return null;
    }

    public async Task<bool> UpdateConfiguration(ObjectId unitIdParsed, UnitConfiguration unitConfiguration)
    {
        var filter = Builders<Unit>.Filter.Eq("_id", unitIdParsed);
        var update = Builders<Unit>.Update
            .Set(c => c.Configuration, unitConfiguration);
        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            return result.ModifiedCount > 0;
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateConfiguration from unit: {name}. Exception: {ex}", unitIdParsed, ex);
            return false;
        }
    }

    #endregion

    #region Delete

    public new async Task<Unit?> DeleteAsync(ObjectId id)
    {
        var filter = Builders<Unit>.Filter.Eq(unit => unit.Id, id);

        try
        {
            return await Collection.FindOneAndDeleteAsync(filter);
        }
        catch (Exception e)
        {
            Log.Error(e.Message);
            return null;
        }
    }

    #endregion

    #endregion
}