using System.Text.Json;
using TakeoutSaaS.Application.App.Billings.Dto;
using TakeoutSaaS.Domain.Tenants.Entities;

namespace TakeoutSaaS.Application.App.Billings;

/// <summary>
/// 账单 DTO 映射助手。
/// </summary>
internal static class BillingMapping
{
    /// <summary>
    /// 将账单实体映射为账单 DTO（旧版）。
    /// </summary>
    /// <param name="bill">账单实体。</param>
    /// <param name="tenantName">租户名称。</param>
    /// <returns>账单 DTO。</returns>
    public static BillDto ToDto(this TenantBillingStatement bill, string? tenantName = null)
        => new()
        {
            Id = bill.Id,
            TenantId = bill.TenantId,
            TenantName = tenantName,
            StatementNo = bill.StatementNo,
            PeriodStart = bill.PeriodStart,
            PeriodEnd = bill.PeriodEnd,
            AmountDue = bill.AmountDue,
            AmountPaid = bill.AmountPaid,
            Status = bill.Status,
            DueDate = bill.DueDate,
            CreatedAt = bill.CreatedAt
        };

    /// <summary>
    /// 将账单实体映射为账单列表 DTO（新版）。
    /// </summary>
    /// <param name="billing">账单实体。</param>
    /// <param name="tenantName">租户名称。</param>
    /// <returns>账单列表 DTO。</returns>
    public static BillingListDto ToBillingListDto(this TenantBillingStatement billing, string? tenantName = null)
        => new()
        {
            Id = billing.Id,
            TenantId = billing.TenantId,
            SubscriptionId = billing.SubscriptionId,
            TenantName = tenantName ?? string.Empty,
            StatementNo = billing.StatementNo,
            BillingType = billing.BillingType,
            Status = billing.Status,
            PeriodStart = billing.PeriodStart,
            PeriodEnd = billing.PeriodEnd,
            AmountDue = billing.AmountDue,
            AmountPaid = billing.AmountPaid,
            DiscountAmount = billing.DiscountAmount,
            TaxAmount = billing.TaxAmount,
            TotalAmount = billing.CalculateTotalAmount(),
            Currency = billing.Currency,
            DueDate = billing.DueDate,
            CreatedAt = billing.CreatedAt,
            UpdatedAt = billing.UpdatedAt,
            IsOverdue = billing.Status == TakeoutSaaS.Domain.Tenants.Enums.TenantBillingStatus.Overdue
                || (billing.Status == TakeoutSaaS.Domain.Tenants.Enums.TenantBillingStatus.Pending && billing.DueDate < DateTime.UtcNow),
            OverdueDays = (billing.Status is TakeoutSaaS.Domain.Tenants.Enums.TenantBillingStatus.Pending
                    or TakeoutSaaS.Domain.Tenants.Enums.TenantBillingStatus.Overdue)
                && billing.DueDate < DateTime.UtcNow
                    ? (int)(DateTime.UtcNow - billing.DueDate).TotalDays
                    : 0
        };

    /// <summary>
    /// 将账单实体与支付记录映射为账单详情 DTO（旧版）。
    /// </summary>
    /// <param name="bill">账单实体。</param>
    /// <param name="payments">支付记录列表。</param>
    /// <param name="tenantName">租户名称。</param>
    /// <returns>账单详情 DTO。</returns>
    public static BillDetailDto ToDetailDto(
        this TenantBillingStatement bill,
        List<TenantPayment> payments,
        string? tenantName = null)
        => new()
        {
            Id = bill.Id,
            TenantId = bill.TenantId,
            TenantName = tenantName,
            StatementNo = bill.StatementNo,
            PeriodStart = bill.PeriodStart,
            PeriodEnd = bill.PeriodEnd,
            AmountDue = bill.AmountDue,
            AmountPaid = bill.AmountPaid,
            Status = bill.Status,
            DueDate = bill.DueDate,
            LineItemsJson = bill.LineItemsJson,
            CreatedAt = bill.CreatedAt,
            Payments = payments.Select(p => p.ToDto()).ToList()
        };

    /// <summary>
    /// 将账单实体与支付记录映射为账单详情 DTO（新版）。
    /// </summary>
    /// <param name="billing">账单实体。</param>
    /// <param name="payments">支付记录列表。</param>
    /// <param name="tenantName">租户名称。</param>
    /// <returns>账单详情 DTO。</returns>
    public static BillingDetailDto ToBillingDetailDto(
        this TenantBillingStatement billing,
        List<TenantPayment> payments,
        string? tenantName = null)
    {
        // 反序列化账单明细
        var lineItems = new List<BillingLineItemDto>();
        if (!string.IsNullOrWhiteSpace(billing.LineItemsJson))
        {
            try
            {
                lineItems = JsonSerializer.Deserialize<List<BillingLineItemDto>>(billing.LineItemsJson) ?? [];
            }
            catch
            {
                lineItems = [];
            }
        }

        return new BillingDetailDto
        {
            Id = billing.Id,
            TenantId = billing.TenantId,
            TenantName = tenantName ?? string.Empty,
            SubscriptionId = billing.SubscriptionId,
            StatementNo = billing.StatementNo,
            BillingType = billing.BillingType,
            Status = billing.Status,
            PeriodStart = billing.PeriodStart,
            PeriodEnd = billing.PeriodEnd,
            AmountDue = billing.AmountDue,
            AmountPaid = billing.AmountPaid,
            DiscountAmount = billing.DiscountAmount,
            TaxAmount = billing.TaxAmount,
            TotalAmount = billing.CalculateTotalAmount(),
            Currency = billing.Currency,
            DueDate = billing.DueDate,
            ReminderSentAt = billing.ReminderSentAt,
            OverdueNotifiedAt = billing.OverdueNotifiedAt,
            LineItemsJson = billing.LineItemsJson,
            LineItems = lineItems,
            Payments = payments.Select(p => p.ToPaymentRecordDto()).ToList(),
            Notes = billing.Notes,
            CreatedAt = billing.CreatedAt,
            CreatedBy = billing.CreatedBy,
            UpdatedAt = billing.UpdatedAt,
            UpdatedBy = billing.UpdatedBy
        };
    }

    /// <summary>
    /// 将支付记录实体映射为支付 DTO（旧版）。
    /// </summary>
    /// <param name="payment">支付记录实体。</param>
    /// <returns>支付 DTO。</returns>
    public static PaymentDto ToDto(this TenantPayment payment)
        => new()
        {
            Id = payment.Id,
            BillingStatementId = payment.BillingStatementId,
            Amount = payment.Amount,
            Method = payment.Method,
            Status = payment.Status,
            TransactionNo = payment.TransactionNo,
            ProofUrl = payment.ProofUrl,
            PaidAt = payment.PaidAt,
            Notes = payment.Notes,
            CreatedAt = payment.CreatedAt
        };

    /// <summary>
    /// 将支付记录实体映射为支付记录 DTO（新版）。
    /// </summary>
    /// <param name="payment">支付记录实体。</param>
    /// <returns>支付记录 DTO。</returns>
    public static PaymentRecordDto ToPaymentRecordDto(this TenantPayment payment)
        => new()
        {
            Id = payment.Id,
            TenantId = payment.TenantId,
            BillingId = payment.BillingStatementId,
            Amount = payment.Amount,
            Method = payment.Method,
            Status = payment.Status,
            TransactionNo = payment.TransactionNo,
            ProofUrl = payment.ProofUrl,
            IsVerified = payment.VerifiedAt.HasValue,
            PaidAt = payment.PaidAt,
            VerifiedBy = payment.VerifiedBy,
            VerifiedAt = payment.VerifiedAt,
            RefundReason = payment.RefundReason,
            RefundedAt = payment.RefundedAt,
            Notes = payment.Notes,
            CreatedAt = payment.CreatedAt
        };
}
