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

namespace adas_core.Infrastructure.Repositories;

public class AppointmentRepository : MongoRepository<PatientAppointment>, IAppointmentRepository
{
    private readonly ApiSettings _apiSettings;

    public AppointmentRepository(
        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.PatientsAppointments ?? "patients_appointments";
    }

    public async Task<List<PatientAppointment>> GetByPatient(ObjectId patientId)
    {
        var filter = Builders<PatientAppointment>.Filter.Eq(ob => ob.PatientId, patientId);
        var options = new FindOptions<PatientAppointment>
        {
            Sort = Builders<PatientAppointment>.Sort.Descending("createTime")
        };

        var result = await Collection.FindAsync(filter, options);

        return result.ToList();
    }
    public async Task<List<PatientAppointment>> FindByPoC(PointOfCare poc)
    {
        var location = new PatientLocation()
        {
            Bed = poc.Bed,
            Room = poc.Room,
            UnitName = poc.UnitName
        };
        return await FindByLocation(location);
    }


    public override async Task InsertOneAsync(PatientAppointment appointment)
    {
        appointment.CreateTime ??= DateTime.UtcNow;
        await base.InsertOneAsync(appointment);
    }

    public async Task Update(PatientAppointment appointment)
    {
        await UpdateOneAsync(appointment.Id, appointment);
    }

    public new async Task DeleteAsync(ObjectId id)
    {
        var filter = Builders<PatientAppointment>.Filter.Eq(t => t.Id, id); // Replace 'T' with your actual class name.
        await Collection.DeleteOneAsync(filter);
    }


    public Task<IAsyncCursor<PatientAppointment>> FindByPatientIdAsync(ObjectId patientId)
    {
        var filter = Builders<PatientAppointment>.Filter.Eq(ob => ob.PatientId, patientId);
        return Collection.FindAsync(filter);
    }


    public async Task DeleteByPatientId(ObjectId patientId)
    {
        var filter = Builders<PatientAppointment>.Filter.Eq(po => po.PatientId, patientId);
        await Collection.DeleteManyAsync(filter);
    }


    public async Task<PatientAppointment?> FindByPatientAndVisitNumber(ObjectId patientId, string visitNumber)
    {
        var builder = Builders<PatientAppointment>.Filter;
        var filter = builder.And(
            builder.Eq(ob => ob.PatientId, patientId),
            builder.Eq(ob => ob.VisitNumber, visitNumber)
        );

        var result = await Collection.FindAsync(filter);

        return await result.FirstOrDefaultAsync();
    }


    public async Task<PatientAppointment?> FindByPatientAndReason(ObjectId patientId, string? appointmentReason)
    {
        var builder = Builders<PatientAppointment>.Filter;
        var filter = builder.And(
            builder.Eq(ob => ob.PatientId, patientId),
            builder.Eq(ob => ob.AppointmentReason, appointmentReason)
        );

        var result = await Collection.FindAsync(filter);

        return await result.FirstOrDefaultAsync();
    }

    public async Task<List<PatientAppointment>> FindByLocation(PatientLocation location)
    {
        var builder = Builders<PatientAppointmentResourceGroup>.Filter;
        var existsFilter = builder.Exists(rg => rg.Locations);
        var locationFilter = builder.ElemMatch(rg => rg.Locations,
            l => l.Bed == location.Bed && l.UnitName == location.UnitName);

        var combinedFilter = builder.And(existsFilter, locationFilter);

        var filter = Builders<PatientAppointment>.Filter.ElemMatch(pa => pa.ResourceGroups, combinedFilter);

        var result = await Collection.FindAsync(filter);
        return result?.ToList()??[];
    }

    public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
    {
        await UpdateManyObjectIdAsync(nameId, id, oldId);
    }

    public override async Task CreateIndexes()
    {
        var options = new CreateIndexOptions { Background = true, Unique = false };
        var indexes = new List<CreateIndexModel<PatientAppointment>>
        {
            new("{ patientid: 1 }", options)
        };

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