using Microsoft.Extensions.Logging;

using DomainResults.Common;

using MongoDB.Driver;

using DataProviders.Abstractions;
using MongoDB.Driver.GridFS;

namespace DataProviders.Buckets {

  /// <summary>
  /// 
  /// </summary>
  public interface IImageBucketDataProvider {

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="userId"></param>
    /// <param name="file"></param>
    /// <returns></returns>
    (Guid?, IDomainResult) Upload(BucketFile file);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="userId"></param>
    /// <param name="files"></param>
    /// <returns></returns>
    (List<Guid>?, IDomainResult) UploadMany(List<BucketFile> files);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="fileId"></param>
    /// <returns></returns>
    (BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="userId"></param>
    /// <param name="fileId"></param>
    /// <returns></returns>
    (BucketFile?, IDomainResult) Download(Guid siteId, Guid userId, Guid fileId);


    (List<BucketFile>?, IDomainResult) DownloadMany(Guid siteId, Guid userId, List<Guid> fileIds);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="userId"></param>
    /// <param name="fileId"></param>
    /// <returns></returns>
    IDomainResult DeleteOne(Guid siteId, Guid userId, Guid fileId);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="userId"></param>
    /// <returns></returns>
    IDomainResult DeleteMany(Guid siteId, Guid userId);
  }

  /// <summary>
  /// 
  /// </summary>
  public class ImageBucketDataProvider : BucketDataProviderBase, IImageBucketDataProvider {

    /// <summary>
    /// 
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="client"></param>
    public ImageBucketDataProvider(
      ILogger<BucketDataProviderBase> logger,
      IMongoClient client
    ) : base(logger, client, "images") { }


    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="fileId"></param>
    /// <returns></returns>
    public (BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId) {
      var filter = Builders<GridFSFileInfo>.Filter.And(
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
        Builders<GridFSFileInfo>.Filter.Not(Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["published"], "")),
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
      );

      var (list, result) = Download(filter);

      if (!result.IsSuccess || list == null)
        return (null, result);

      return (list.First(), result);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="userId"></param>
    /// <param name="fileId"></param>
    /// <returns></returns>
    public (BucketFile?, IDomainResult) Download(Guid siteId, Guid userId, Guid fileId) {

      var filter = Builders<GridFSFileInfo>.Filter.And(
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}"),
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
      );

      var (list, result) = Download(filter);

      if (!result.IsSuccess || list == null)
        return (null, result);

      return (list.First(), result);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="userId"></param>
    /// <param name="fileId"></param>
    /// <returns></returns>
    public (List<BucketFile>?, IDomainResult) DownloadMany(Guid siteId, Guid userId, List<Guid> fileIds) {

      var builder = Builders<GridFSFileInfo>.Filter;

      FilterDefinition<GridFSFileInfo>? filter = default;

      foreach (var fileId in fileIds) {
        filter |= builder.And(
          Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
          Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}"),
          Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
        );
      }

      var result = Download(filter);

      return result;
    }

    public IDomainResult DeleteOne(Guid siteId, Guid userId, Guid fileId) =>
      DeleteOneAsync(siteId, userId, fileId).Result;

    public Task<IDomainResult> DeleteOneAsync(Guid siteId, Guid userId, Guid fileId) =>
      DeleteAsync(Builders<GridFSFileInfo>.Filter.And(
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}"),
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
      ));

    public IDomainResult DeleteMany(Guid siteId, Guid userId) =>
      DeleteManyAsync(siteId, userId).Result;

    public Task<IDomainResult> DeleteManyAsync(Guid siteId, Guid userId) =>
      DeleteAsync(Builders<GridFSFileInfo>.Filter.And(
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
        Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}")
      ));
  }
}
