using Microsoft.Extensions.Logging;

using MongoDB.Driver;
using MongoDB.Bson.Serialization;

using DomainResults.Common;

using DataProviders.Abstractions;
using Core.DomainObjects.Documents;
using Core.DomainObjects.L10n;
using Core.Enumerations;

namespace DataProviders.Collections {

  public interface ICategoryDataProvider {
    (Guid?, IDomainResult) Insert(Category obj);
    (Guid?, IDomainResult) CreateDefault(Guid siteId);
    (Category?, IDomainResult) Get(Guid siteId, Guid categoryId);
    (List<Category>?, IDomainResult) GetMany(Guid siteId, List<Guid> categoryIds);
    (Category?, IDomainResult) GetBySlug(Guid siteId, string slug);
    (List<Category>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs);
    (List<Category>?, IDomainResult) GetAll(Guid siteId);
    (Guid?, IDomainResult) Update(Category obj);
    IDomainResult Delete(Guid id);
    IDomainResult DeleteAll(Guid siteId);
  }

  public class CategoryDataProvider : CollectionDataProviderBase<Category>, ICategoryDataProvider {

    private const string _collectionName = "categories";
    public CategoryDataProvider(
      ILogger<CollectionDataProviderBase<Category>> logger,
      IMongoClient client,
      IIdGenerator idGenerator,
      ISessionService sessionService) : base(logger, client, idGenerator, sessionService) {
    }

    public (Guid?, IDomainResult) Insert(Category obj) =>
      Insert(obj, _collectionName);

    public (Guid?, IDomainResult) CreateDefault(Guid siteId) {
      var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => y.Slug == "default"), _collectionName);

      if (result.IsSuccess && list != null)
        return (list.First().Id, result);

      return Insert(new Category {
        SiteId = siteId,
        L10n = new List<CategoryL10n> {
          new CategoryL10n {
            Locale = Locales.Us,
            Slug = "default",
            Text = "Default category"
          }
        }
      });
    }

    public (Category?, IDomainResult) Get(Guid siteId, Guid categoryId) {
      var (list, result) = GetWithPredicate(x => x.SiteId == siteId && x.Id == categoryId, _collectionName);

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

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

    public (List<Category>?, IDomainResult) GetMany(Guid siteId, List<Guid> categoryIds) =>
      GetWithPredicate(x => x.SiteId == siteId && categoryIds.Contains(x.Id), _collectionName);

    public (Category?, IDomainResult) GetBySlug(Guid siteId, string slug) {
      var (list, result) = GetBySlugs(siteId, new List<string> { slug });

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

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

    public (List<Category>?, IDomainResult) GetBySlugs(Guid siteId, List<string> slugs) =>
      GetWithPredicate(x => x.SiteId == siteId && x.L10n.Any(y => slugs.Contains(y.Slug)), _collectionName);

    public (List<Category>?, IDomainResult) GetAll(Guid siteId) =>
      GetWithPredicate(x => x.SiteId == siteId, _collectionName);

    public (Guid?, IDomainResult) Update(Category obj) =>
      UpdateWithPredicate(obj, x => x.Id == obj.Id, _collectionName);

    public IDomainResult Delete(Guid id) =>
      DeleteWithPredicate(x => x.Id == id, _collectionName);

    public IDomainResult DeleteAll(Guid siteId) =>
      DeleteManyWithPredicate(x => x.SiteId == siteId, _collectionName);
  }
}
