using MediatR;
using TakeoutSaaS.Application.App.Finance.Invoice.Commands;
using TakeoutSaaS.Application.App.Finance.Invoice.Dto;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Security;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Finance.Invoice.Handlers;

/// <summary>
/// 发票开票处理器。
/// </summary>
public sealed class IssueFinanceInvoiceRecordCommandHandler(
    ITenantInvoiceRepository repository,
    ITenantProvider tenantProvider,
    ICurrentUserAccessor currentUserAccessor)
    : IRequestHandler<IssueFinanceInvoiceRecordCommand, FinanceInvoiceIssueResultDto>
{
    /// <inheritdoc />
    public async Task<FinanceInvoiceIssueResultDto> Handle(
        IssueFinanceInvoiceRecordCommand request,
        CancellationToken cancellationToken)
    {
        var tenantId = tenantProvider.GetCurrentTenantId();
        var record = await repository.FindRecordByIdAsync(tenantId, request.RecordId, cancellationToken)
                     ?? throw new BusinessException(ErrorCodes.NotFound, "发票记录不存在");

        if (record.Status != TenantInvoiceStatus.Pending)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "仅待开票记录允许开票");
        }

        var setting = await repository.GetSettingAsync(tenantId, cancellationToken)
                      ?? throw new BusinessException(ErrorCodes.BadRequest, "请先完成发票设置");
        EnsureTypeEnabled(setting, record.InvoiceType);

        record.ContactEmail = FinanceInvoiceMapping.NormalizeOptionalEmail(request.ContactEmail) ?? record.ContactEmail;
        record.IssueRemark = FinanceInvoiceMapping.NormalizeOptionalRemark(request.IssueRemark, "issueRemark");
        record.Status = TenantInvoiceStatus.Issued;
        record.IssuedAt = DateTime.UtcNow;
        record.IssuedByUserId = currentUserAccessor.IsAuthenticated ? currentUserAccessor.UserId : null;

        await repository.UpdateRecordAsync(record, cancellationToken);
        await repository.SaveChangesAsync(cancellationToken);

        return FinanceInvoiceDtoFactory.ToIssueResultDto(record);
    }

    private static void EnsureTypeEnabled(TenantInvoiceSetting setting, TenantInvoiceType type)
    {
        if (type == TenantInvoiceType.Normal && !setting.EnableElectronicNormalInvoice)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "电子普通发票未启用");
        }

        if (type == TenantInvoiceType.Special && !setting.EnableElectronicSpecialInvoice)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "电子专用发票未启用");
        }
    }
}
