using DomainResults.Common;

using DataProviders.Collections;

using Core.Enumerations;

using WeatherForecast.Models.Requests;
using WeatherForecast.Models.Responses;
using Core.Abstractions;

namespace WeatherForecast.Services {

  /// <summary>
  /// 
  /// </summary>
  public interface ICategoryItemService {

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="requestModel"></param>
    /// <returns></returns>
    (Guid?, IDomainResult) Post(Guid siteId, CategoryItemRequestModel requestModel);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="categoryId"></param>
    /// <returns></returns>
    (CategoryItemResponseModel?, IDomainResult) Get(Guid siteId, Guid categoryId);

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

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="categoryId"></param>
    /// <param name="requestData"></param>
    /// <returns></returns>
    (Guid?, IDomainResult) Update(Guid siteId, Guid categoryId, CategoryItemRequestModel requestData);

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

  /// <summary>
  /// 
  /// </summary>
  public class CategoryItemService : ServiceBase<CategoryItemService>, ICategoryItemService {

    private readonly ICategoryDataProvider _categoryDataProvider;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="categoryDataProvider"></param>
    public CategoryItemService(
      ILogger<CategoryItemService> logger,
      ICategoryDataProvider categoryDataProvider
    ) : base(logger) {
      _categoryDataProvider = categoryDataProvider;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="requestModel"></param>
    /// <returns></returns>
    public (Guid?, IDomainResult) Post(Guid siteId, CategoryItemRequestModel requestModel) {
      try {
        var item = requestModel.ToDomainObject();
        item.SiteId = siteId;

        var (_, getResult) = _categoryDataProvider.GetBySlugs(item.SiteId, item.L10n.Select(x => x.Slug).ToList());
        if (getResult.IsSuccess)
          return IDomainResult.Failed<Guid?>();

        var (id, insertResult) = _categoryDataProvider.Insert(item);

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

        return IDomainResult.Success(id);
      }
      catch (Exception ex) {
        _logger.LogError(ex, "Unhandled exception");
        return IDomainResult.Failed<Guid?>(ex.Message);
      }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="categoryId"></param>
    /// <returns></returns>
    public (CategoryItemResponseModel?, IDomainResult) Get(Guid siteId, Guid categoryId) {
      try {
        var (item, result) = _categoryDataProvider.Get(siteId, categoryId);
        if (!result.IsSuccess || item == null)
          return (null, result);

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

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="slug"></param>
    /// <returns></returns>
    public (CategoryItemResponseModel?, IDomainResult) GetSlug(Guid siteId, string slug) {
      try {
        var (item, result) = _categoryDataProvider.GetBySlug(siteId, slug);
        if (!result.IsSuccess || item == null)
          return (null, result);

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

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

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="categoryId"></param>
    /// <param name="requestData"></param>
    /// <returns></returns>
    public (Guid?, IDomainResult) Update(Guid siteId, Guid categoryId, CategoryItemRequestModel requestData) {
      try {
        var (item, result) = _categoryDataProvider.Get(siteId, categoryId);
        if (!result.IsSuccess || item == null)
          return (null, result);

        // construct domain object from model
        var newItem = requestData.ToDomainObject();
        newItem.Id = item.Id;
        newItem.SiteId = item.SiteId;

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

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

    /// <summary>
    /// 
    /// </summary>
    /// <param name="siteId"></param>
    /// <param name="categoryId"></param>
    /// <returns></returns>
    public IDomainResult Delete(Guid siteId, Guid categoryId) {
      try {
        var (item, getResult) = _categoryDataProvider.Get(siteId, categoryId);
        if (!getResult.IsSuccess || item == null)
          return getResult;

        var result = _categoryDataProvider.Delete(item.Id);
        if (!result.IsSuccess)
          return result;

        return IDomainResult.Success();
      }
      catch (Exception ex) {
        _logger.LogError(ex, "Unhandled exception");
        return IDomainResult.Failed(ex.Message);
      }
    }
  }
}
