using MediatR;
using TakeoutSaaS.Application.App.Coupons.NewCustomer.Commands;
using TakeoutSaaS.Application.App.Coupons.NewCustomer.Dto;
using TakeoutSaaS.Domain.Coupons.Entities;
using TakeoutSaaS.Domain.Coupons.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Coupons.NewCustomer.Handlers;

/// <summary>
/// 写入新客成长记录处理器。
/// </summary>
public sealed class WriteNewCustomerGrowthRecordCommandHandler(
    INewCustomerGiftRepository repository,
    ITenantProvider tenantProvider)
    : IRequestHandler<WriteNewCustomerGrowthRecordCommand, NewCustomerGrowthRecordDto>
{
    /// <inheritdoc />
    public async Task<NewCustomerGrowthRecordDto> Handle(
        WriteNewCustomerGrowthRecordCommand request,
        CancellationToken cancellationToken)
    {
        if (request.StoreId <= 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "storeId 参数不合法");
        }

        var tenantId = tenantProvider.GetCurrentTenantId();
        var customerKey = NewCustomerMapping.NormalizeCustomerKey(request.CustomerKey);
        var customerName = NewCustomerMapping.NormalizeOptionalText(request.CustomerName, "customerName", 64);
        var sourceChannel = NewCustomerMapping.NormalizeOptionalText(request.SourceChannel, "sourceChannel", 32);
        var registeredAt = NewCustomerMapping.NormalizeUtc(request.RegisteredAt);
        DateTime? giftClaimedAt = request.GiftClaimedAt.HasValue
            ? NewCustomerMapping.NormalizeUtc(request.GiftClaimedAt.Value)
            : null;
        DateTime? firstOrderAt = request.FirstOrderAt.HasValue
            ? NewCustomerMapping.NormalizeUtc(request.FirstOrderAt.Value)
            : null;

        var entity = await repository.FindGrowthRecordByCustomerKeyAsync(
            tenantId: tenantId,
            storeId: request.StoreId,
            customerKey: customerKey,
            cancellationToken: cancellationToken);

        if (entity is null)
        {
            entity = new NewCustomerGrowthRecord
            {
                StoreId = request.StoreId,
                CustomerKey = customerKey,
                CustomerName = customerName,
                RegisteredAt = registeredAt,
                GiftClaimedAt = giftClaimedAt,
                FirstOrderAt = firstOrderAt,
                SourceChannel = sourceChannel
            };

            await repository.AddGrowthRecordAsync(entity, cancellationToken);
            await repository.SaveChangesAsync(cancellationToken);
            return NewCustomerMapping.ToGrowthRecordDto(entity);
        }

        if (!string.IsNullOrWhiteSpace(customerName))
        {
            entity.CustomerName = customerName;
        }

        if (!string.IsNullOrWhiteSpace(sourceChannel))
        {
            entity.SourceChannel = sourceChannel;
        }

        entity.RegisteredAt = registeredAt < entity.RegisteredAt
            ? registeredAt
            : entity.RegisteredAt;
        entity.GiftClaimedAt = MergeNullableDate(entity.GiftClaimedAt, giftClaimedAt);
        entity.FirstOrderAt = MergeNullableDate(entity.FirstOrderAt, firstOrderAt);

        await repository.UpdateGrowthRecordAsync(entity, cancellationToken);
        await repository.SaveChangesAsync(cancellationToken);
        return NewCustomerMapping.ToGrowthRecordDto(entity);
    }

    private static DateTime? MergeNullableDate(DateTime? existing, DateTime? incoming)
    {
        if (!existing.HasValue)
        {
            return incoming;
        }

        if (!incoming.HasValue)
        {
            return existing;
        }

        return incoming.Value < existing.Value ? incoming : existing;
    }
}
