using DomainResults.Common;

using DataProviders.Collections;

using WeatherForecast.Models.Requests;
using WeatherForecast.Services.Abstractions;
using DomainObjects.Documents;
using WeatherForecast.Models.Shop.Responses;
using DomainObjects.Enumerations;

namespace WeatherForecast.Services {

  /// <summary>
  /// 
  /// </summary>
  public interface IShopItemService {
    
    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="sku"></param>
    /// <param name="requestModel"></param>
    /// <returns></returns>
    (Guid?, IDomainResult) Post(ShopDocument shopItem);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="sku"></param>
    /// <returns></returns>
    (ShopItemResponseModel?, IDomainResult) Get(ShopDocument shopItem);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="slug"></param>
    /// <returns></returns>
    (ShopItemResponseModel?, IDomainResult) GetSlug(Guid siteId, string slug);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="sku"></param>
    /// <param name="requestData"></param>
    /// <returns></returns>
    (Guid?, IDomainResult) Update(Guid siteId, string sku, ShopItemRequestModel requestData);
  }

  /// <summary>
  /// 
  /// </summary>
  public class ShopItemService : PostItemServiceBase<ShopItemService>, IShopItemService {

    private readonly IShopCatalogDataProvider _shopCatalogDataProvider;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="shopCatalogDataProvider"></param>
    /// <param name="categoryDataProvider"></param>
    public ShopItemService(
      ILogger<ShopItemService> logger,
      IShopCatalogDataProvider shopCatalogDataProvider,
      ICategoryDataProvider categoryDataProvider
    ) : base(logger, categoryDataProvider) {
      _shopCatalogDataProvider = shopCatalogDataProvider;
      
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="shopItem"></param>
    /// <returns></returns>
    public (Guid?, IDomainResult) Post(ShopDocument shopItem) {

      var (_, getResult) = _shopCatalogDataProvider.Get(shopItem.SiteId, shopItem.Sku);
      if (getResult.IsSuccess)
        return IDomainResult.Failed<Guid?>();

      var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(shopItem.SiteId, shopItem.Categories);
      if (!addCategoriesResult.IsSuccess || categories == null)
        return (null, addCategoriesResult);

      shopItem.Categories = categories;

      var (id, insertResult) = _shopCatalogDataProvider.Insert(shopItem);

      if (!insertResult.IsSuccess)
        return IDomainResult.Failed<Guid?>();

      return IDomainResult.Success(id);
  
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="sku"></param>
    /// <returns></returns>
    public (ShopItemResponseModel?, IDomainResult) Get(ShopDocument shopItem) {
      var (categories, getCategoryResult) = _categoryDataProvider.GetMany(shopItem.SiteId, shopItem.Categories);
      if (!getCategoryResult.IsSuccess || categories == null)
        return IDomainResult.Failed<ShopItemResponseModel?>();

      return IDomainResult.Success(new ShopItemResponseModel(shopItem, categories));
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="slug"></param>
    /// <returns></returns>
    public (ShopItemResponseModel?, IDomainResult) GetSlug(Guid siteId, string slug) {
      try {
        var (item, result) = _shopCatalogDataProvider.GetBySlug(siteId, slug);

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

        var locale = item.L10n.SingleOrDefault(x => x.Slug == slug)?.Locale ?? Locales.Us;

        var (categories, getCategoryResult) = _categoryDataProvider.GetMany(siteId, item.Categories);
        if (!getCategoryResult.IsSuccess || categories == null)
          return IDomainResult.Failed<ShopItemResponseModel?>();

        return IDomainResult.Success(new ShopItemResponseModel(item, categories, locale));
      }
      catch (Exception ex) {
        _logger.LogError(ex, "Unhandled exception");
        return IDomainResult.Failed<ShopItemResponseModel?>(ex.Message);
      }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="sku"></param>
    /// <param name="requestData"></param>
    /// <returns></returns>
    public (Guid?, IDomainResult) Update(Guid siteId, string sku, ShopItemRequestModel requestData) {

      var (item, getResult) = _shopCatalogDataProvider.Get(siteId, sku);
      if (!getResult.IsSuccess || item == null)
        return (null, getResult);

      // construct domain object from model
      var newItem = requestData.ToDomainObject();
      newItem.Id = item.Id;
      newItem.SiteId = siteId;
      newItem.Sku = sku;
      newItem.Created = item.Created;
      newItem.Author = item.Author;

      var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, requestData.Categories);
      if (!addCategoriesResult.IsSuccess || categories == null)
        return (null, addCategoriesResult);

      item.Categories = categories;

      if (!item.Equals(newItem)) {
        var (id, updateResult) = _shopCatalogDataProvider.Update(newItem);
        if (!updateResult.IsSuccess || id == null)
          return (null, updateResult);
      }

      return IDomainResult.Success(item.Id);

    }
  }
}
