using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;

namespace adas_core.Application.Services;

public class CameraService(ILogger<CameraService> logger, ICameraRepository cameraRepository, IPointOfCareService pointOfCareService) : ICameraService
{
    private ICameraRepository _cameraRepository = cameraRepository;
    public Task<Camera?> GetById(ObjectId relayId)
    {
        return _cameraRepository.GetById(relayId);
    }
    public List<Camera> GetCameraInList(List<ObjectId> configurationRelayList)
    {
        return _cameraRepository.GetCameraInList(configurationRelayList);
    }

    public async Task<PaginationResponse<Camera>> GetPaginatedCameras(PaginationFilter filter)
    {
        var usedCameraIds = await pointOfCareService.FindAllIdCamerasInUse();

        var fluentQuery = _cameraRepository.GetPaginatedCameras(filter);

        if (filter.FilteredRequest?.InUse != null)
        {
            bool filterInUse = filter.FilteredRequest.InUse.Value;
            var filterBuilder = Builders<Camera>.Filter;
        
            var idFilter = filterInUse 
                ? filterBuilder.In(c => c.Id, usedCameraIds)
                : filterBuilder.Not(filterBuilder.In(c => c.Id, usedCameraIds));

            fluentQuery.Filter = filterBuilder.And(fluentQuery.Filter, idFilter);
        }

        var count = await fluentQuery.CountDocumentsAsync();
        var data = await fluentQuery
            .Skip((filter.PageNumber - 1) * filter.PageSize)
            .Limit(filter.PageSize)
            .ToListAsync();

        if(data == null) return new PaginationResponse<Camera>([], filter.PageNumber, filter.PageSize, count);

        foreach (var camera in data)
        {
            if (camera == null) continue;

            bool isInUse = usedCameraIds.Contains(camera.Id);
        
            // Asignación mediante reflexión para el private set
            camera.GetType().GetProperty(nameof(Camera.InUse))
                ?.SetValue(camera, isInUse);
        }
        
        return new PaginationResponse<Camera>(data, filter.PageNumber, filter.PageSize, count);
    }

    public async Task<Camera?> InsertCamera(Camera camera)
    {
        if (camera.Name == null) throw new Exception("Camera name cannot be null");
        var cameraFound = await _cameraRepository.GetByName(camera.Name);
        if(cameraFound != null) throw new Exception($"Camera with name {camera.Name} already exists");
        return await _cameraRepository.InsertOneCamera(camera);
    }

    public async Task<Camera?> UpdateCameraById(ObjectId objectId, Camera camera)
    {
        return await _cameraRepository.UpdateCameraAsync(objectId, camera);
    }

    public async Task<bool> DeleteCamera(ObjectId objectId)
    {
        try
        {
            var cameraToDelete = await _cameraRepository.GetById(objectId);
            if(cameraToDelete == null) return false;
        
            await _cameraRepository.DeleteAsync(cameraToDelete.Id);
            return true;
        }
        catch (Exception e)
        {
            logger.LogError(e, e.Message);
            return false;
        }
        
    }

    public async Task<List<Camera>> GetSearchByNameCameras(string textToSearch)
    {
        return await _cameraRepository.GetSearchByNameCameras(textToSearch);
    }
}