using MediatR;
using TakeoutSaaS.Application.App.Tenants.Commands;
using TakeoutSaaS.Application.App.Tenants.Dto;
using TakeoutSaaS.Application.Messaging.Abstractions;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Events;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Tenants.Handlers;

/// <summary>
/// 发布公告处理器。
/// </summary>
public sealed class PublishAnnouncementCommandHandler(
    ITenantAnnouncementRepository announcementRepository,
    ITenantProvider tenantProvider,
    IEventPublisher eventPublisher)
    : IRequestHandler<PublishAnnouncementCommand, TenantAnnouncementDto?>
{
    /// <inheritdoc />
    public async Task<TenantAnnouncementDto?> Handle(PublishAnnouncementCommand request, CancellationToken cancellationToken)
    {
        if (request.RowVersion == null || request.RowVersion.Length == 0)
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "RowVersion 不能为空");
        }

        // 1. 查询公告
        var tenantId = tenantProvider.GetCurrentTenantId();
        var announcement = await announcementRepository.FindByIdAsync(tenantId, request.AnnouncementId, cancellationToken);
        if (announcement == null)
        {
            return null;
        }

        // 2. 校验状态与目标受众
        if (string.IsNullOrWhiteSpace(announcement.TargetType))
        {
            throw new BusinessException(ErrorCodes.ValidationFailed, "目标受众类型不能为空");
        }

        if (announcement.Status == AnnouncementStatus.Published)
        {
            throw new BusinessException(ErrorCodes.Conflict, "公告已发布");
        }

        if (announcement.Status != AnnouncementStatus.Draft && announcement.Status != AnnouncementStatus.Revoked)
        {
            throw new BusinessException(ErrorCodes.Conflict, "仅草稿或已撤销公告允许发布");
        }

        // 3. 发布公告
        announcement.Status = AnnouncementStatus.Published;
        announcement.PublishedAt = DateTime.UtcNow;
        announcement.RevokedAt = null;
        announcement.RowVersion = request.RowVersion;

        try
        {
            await announcementRepository.UpdateAsync(announcement, cancellationToken);
            await announcementRepository.SaveChangesAsync(cancellationToken);
        }
        catch (Exception exception) when (exception.GetType().Name == "DbUpdateConcurrencyException")
        {
            throw new BusinessException(ErrorCodes.Conflict, "公告已被修改，请刷新后重试");
        }

        // 4. 发布领域事件
        await eventPublisher.PublishAsync(
            "tenant-announcement.published",
            new AnnouncementPublished
            {
                AnnouncementId = announcement.Id,
                PublishedAt = announcement.PublishedAt ?? DateTime.UtcNow,
                TargetType = announcement.TargetType
            },
            cancellationToken);

        return announcement.ToDto(false, null);
    }
}
