// EN: Handlers for Shop queries.
// VI: Handlers cho Shop queries.

using MediatR;
using MerchantService.Domain.AggregatesModel.MerchantAggregate;
using MerchantService.Domain.AggregatesModel.ShopAggregate;
using MerchantService.Domain.Exceptions;
using MerchantService.Domain.SeedWork;
using static MerchantService.API.Application.Queries.Shops.EnumResolverHelper;

namespace MerchantService.API.Application.Queries.Shops;

/// <summary>
/// EN: Handler for getting current merchant's shops.
/// VI: Handler để lấy danh sách shops của merchant hiện tại.
/// </summary>
public class GetMyShopsQueryHandler : IRequestHandler<GetMyShopsQuery, IReadOnlyList<ShopDto>>
{
    private readonly IMerchantRepository _merchantRepository;
    private readonly IShopRepository _shopRepository;

    public GetMyShopsQueryHandler(
        IMerchantRepository merchantRepository,
        IShopRepository shopRepository)
    {
        _merchantRepository = merchantRepository;
        _shopRepository = shopRepository;
    }

    public async Task<IReadOnlyList<ShopDto>> Handle(GetMyShopsQuery request, CancellationToken cancellationToken)
    {
        // EN: Use user ID passed from the controller
        // VI: Dùng user ID được truyền từ controller
        var merchant = await _merchantRepository.GetByUserIdAsync(request.UserId, cancellationToken);
        if (merchant == null)
        {
            return [];
        }

        var shops = await _shopRepository.GetByMerchantIdAsync(merchant.Id, cancellationToken);

        return shops.Select(s => new ShopDto
        {
            Id = s.Id,
            Name = s.Name,
            Slug = s.Slug,
            Type = ResolveEnumName<ShopType>(s.TypeId, "Unknown"),
            Category = ResolveEnumName<BusinessCategory>(s.CategoryId, "Other"),
            Status = ResolveEnumName<ShopStatus>(s.StatusId, "Unknown"),
            LogoUrl = s.LogoUrl,
            IsDefault = s.IsDefault,
            BranchCount = s.Branches?.Count ?? 0,
            CreatedAt = s.CreatedAt
        }).ToList();
    }
}

/// <summary>
/// EN: Handler for getting shop by ID.
/// VI: Handler để lấy shop theo ID.
/// </summary>
public class GetShopByIdQueryHandler : IRequestHandler<GetShopByIdQuery, ShopDetailDto?>
{
    private readonly IShopRepository _shopRepository;

    public GetShopByIdQueryHandler(IShopRepository shopRepository)
    {
        _shopRepository = shopRepository;
    }

    public async Task<ShopDetailDto?> Handle(GetShopByIdQuery request, CancellationToken cancellationToken)
    {
        var shop = await _shopRepository.GetByIdWithBranchesAsync(request.ShopId, cancellationToken);
        return shop != null ? MapToDetailDto(shop) : null;
    }

    private static ShopDetailDto MapToDetailDto(Shop shop)
    {
        return new ShopDetailDto
        {
            Id = shop.Id,
            MerchantId = shop.MerchantId,
            Name = shop.Name,
            Slug = shop.Slug,
            Type = ResolveEnumName<ShopType>(shop.TypeId, "Unknown"),
            Category = ResolveEnumName<BusinessCategory>(shop.CategoryId, "Other"),
            Status = ResolveEnumName<ShopStatus>(shop.StatusId, "Unknown"),
            Description = shop.Description,
            LogoUrl = shop.LogoUrl,
            CoverImageUrl = shop.CoverImageUrl,
            IsDefault = shop.IsDefault,
            ContactInfo = shop.ContactInfo != null ? new ContactInfoDto
            {
                Phone = shop.ContactInfo.Phone,
                Email = shop.ContactInfo.Email,
                Website = shop.ContactInfo.Website
            } : new ContactInfoDto(),
            OperatingHours = shop.OperatingHours != null ? new OperatingHoursDto
            {
                OpenTime = shop.OperatingHours.OpenTime.ToString("HH:mm"),
                CloseTime = shop.OperatingHours.CloseTime.ToString("HH:mm"),
                OpenDays = shop.OperatingHours.OpenDays?.Select(d => d.ToString()).ToList() ?? new()
            } : null,
            Branches = shop.Branches?.Select(b => new ShopBranchDto
            {
                Id = b.Id,
                Name = b.Name,
                Code = b.Code,
                Address = b.Address != null ? new AddressDto
                {
                    Street = b.Address.Street,
                    Ward = b.Address.Ward,
                    District = b.Address.District,
                    City = b.Address.City,
                    Province = b.Address.Province,
                    FullAddress = b.Address.FullAddress
                } : new AddressDto(),
                Location = b.Location != null ? new GeoLocationDto
                {
                    Latitude = b.Location.Latitude,
                    Longitude = b.Location.Longitude
                } : null,
                Phone = b.Phone,
                IsActive = b.IsActive
            }).ToList() ?? new(),
            CreatedAt = shop.CreatedAt,
            UpdatedAt = shop.UpdatedAt
        };
    }
}

/// <summary>
/// EN: Handler for getting shop by slug.
/// VI: Handler để lấy shop theo slug.
/// </summary>
public class GetShopBySlugQueryHandler : IRequestHandler<GetShopBySlugQuery, ShopDetailDto?>
{
    private readonly IShopRepository _shopRepository;

    public GetShopBySlugQueryHandler(IShopRepository shopRepository)
    {
        _shopRepository = shopRepository;
    }

    public async Task<ShopDetailDto?> Handle(GetShopBySlugQuery request, CancellationToken cancellationToken)
    {
        var shop = await _shopRepository.GetBySlugAsync(request.Slug, cancellationToken);
        if (shop == null)
        {
            return null;
        }

        // EN: Get shop with branches
        // VI: Lấy shop với các chi nhánh
        var shopWithBranches = await _shopRepository.GetByIdWithBranchesAsync(shop.Id, cancellationToken);
        return shopWithBranches != null ? new ShopDetailDto
        {
            Id = shopWithBranches.Id,
            MerchantId = shopWithBranches.MerchantId,
            Name = shopWithBranches.Name,
            Slug = shopWithBranches.Slug,
            Type = ResolveEnumName<ShopType>(shopWithBranches.TypeId, "Unknown"),
            Category = ResolveEnumName<BusinessCategory>(shopWithBranches.CategoryId, "Other"),
            Status = ResolveEnumName<ShopStatus>(shopWithBranches.StatusId, "Unknown"),
            Description = shopWithBranches.Description,
            LogoUrl = shopWithBranches.LogoUrl,
            CoverImageUrl = shopWithBranches.CoverImageUrl,
            IsDefault = shopWithBranches.IsDefault,
            ContactInfo = new ContactInfoDto
            {
                Phone = shopWithBranches.ContactInfo.Phone,
                Email = shopWithBranches.ContactInfo.Email,
                Website = shopWithBranches.ContactInfo.Website
            },
            Branches = shopWithBranches.Branches.Where(b => b.IsActive).Select(b => new ShopBranchDto
            {
                Id = b.Id,
                Name = b.Name,
                Code = b.Code,
                Address = new AddressDto
                {
                    Street = b.Address.Street,
                    Ward = b.Address.Ward,
                    District = b.Address.District,
                    City = b.Address.City,
                    Province = b.Address.Province,
                    FullAddress = b.Address.FullAddress
                },
                Location = b.Location != null ? new GeoLocationDto
                {
                    Latitude = b.Location.Latitude,
                    Longitude = b.Location.Longitude
                } : null,
                Phone = b.Phone,
                IsActive = b.IsActive
            }).ToList(),
            CreatedAt = shopWithBranches.CreatedAt,
            UpdatedAt = shopWithBranches.UpdatedAt
        } : null;
    }
}

/// <summary>
/// EN: Helper to resolve Enumeration names from integer IDs.
/// VI: Helper để phân giải tên Enumeration từ integer IDs.
/// </summary>
internal static class EnumResolverHelper
{
    public static string ResolveEnumName<T>(int id, string fallback) where T : Enumeration
    {
        try
        {
            var item = Enumeration.FromValue<T>(id);
            return item?.Name ?? fallback;
        }
        catch
        {
            return fallback;
        }
    }
}
