using System.Globalization;
using System.Text.Json;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Domain.Tenants.Services;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Infrastructure.App.Services;

/// <summary>
/// 账单领域服务实现。
/// </summary>
public sealed class BillingDomainService(
    ITenantBillingRepository billingRepository,
    ITenantRepository tenantRepository,
    ITenantContextAccessor tenantContextAccessor,
    ITenantPackageRepository tenantPackageRepository,
    IIdGenerator idGenerator) : IBillingDomainService
{
    /// <inheritdoc />
    public async Task<TenantBillingStatement> GenerateSubscriptionBillingAsync(
        TenantSubscription subscription,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(subscription);

        // 1. 校验幂等：同一周期开始时间只能存在一张未取消账单
        var exists = await billingRepository.ExistsNotCancelledByPeriodStartAsync(
            subscription.TenantId,
            subscription.EffectiveFrom,
            cancellationToken);
        if (exists)
        {
            throw new InvalidOperationException("该订阅周期的账单已存在。");
        }

        // 2. 查询套餐价格信息
        var package = await tenantPackageRepository.FindByIdAsync(subscription.TenantPackageId, cancellationToken);
        if (package is null)
        {
            throw new InvalidOperationException("订阅未关联有效套餐，无法生成账单。");
        }

        // 3. 选择价格（简化规则：优先按年/按月）
        var days = (subscription.EffectiveTo - subscription.EffectiveFrom).TotalDays;
        var amountDue = days >= 300 ? package.YearlyPrice : package.MonthlyPrice;
        if (!amountDue.HasValue)
        {
            throw new InvalidOperationException("套餐价格未配置，无法生成账单。");
        }

        // 4. 生成账单明细
        var lineItems = new List<BillingLineItem>
        {
            BillingLineItem.Create(
                itemType: "Subscription",
                description: $"套餐 {package.Name} 订阅费用",
                quantity: 1,
                unitPrice: amountDue.Value)
        };

        // 5. 构建账单实体
        var now = DateTime.UtcNow;
        return new TenantBillingStatement
        {
            Id = idGenerator.NextId(),
            TenantId = subscription.TenantId,
            StatementNo = GenerateStatementNo(),
            BillingType = BillingType.Subscription,
            SubscriptionId = subscription.Id,
            PeriodStart = subscription.EffectiveFrom,
            PeriodEnd = subscription.EffectiveTo,
            AmountDue = amountDue.Value,
            DiscountAmount = 0m,
            TaxAmount = 0m,
            AmountPaid = 0m,
            Currency = "CNY",
            Status = TenantBillingStatus.Pending,
            DueDate = now.AddDays(7),
            LineItemsJson = JsonSerializer.Serialize(lineItems),
            Notes = subscription.Notes
        };
    }

    /// <inheritdoc />
    public Task<TenantBillingStatement> GenerateQuotaPurchaseBillingAsync(
        long tenantId,
        QuotaPackage quotaPackage,
        int quantity,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(quotaPackage);

        if (quantity <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(quantity), "购买数量必须大于 0。");
        }

        // 1. 计算金额
        var amountDue = quotaPackage.Price * quantity;

        // 2. 生成账单明细
        var lineItems = new List<BillingLineItem>
        {
            BillingLineItem.Create(
                itemType: "QuotaPurchase",
                description: $"配额包 {quotaPackage.Name} × {quantity}",
                quantity: quantity,
                unitPrice: quotaPackage.Price)
        };

        // 3. 构建账单实体
        var now = DateTime.UtcNow;
        var billing = new TenantBillingStatement
        {
            Id = idGenerator.NextId(),
            TenantId = tenantId,
            StatementNo = GenerateStatementNo(),
            BillingType = BillingType.QuotaPurchase,
            SubscriptionId = null,
            PeriodStart = now,
            PeriodEnd = now,
            AmountDue = amountDue,
            DiscountAmount = 0m,
            TaxAmount = 0m,
            AmountPaid = 0m,
            Currency = "CNY",
            Status = TenantBillingStatus.Pending,
            DueDate = now.AddDays(7),
            LineItemsJson = JsonSerializer.Serialize(lineItems),
            Notes = quotaPackage.Description
        };

        return Task.FromResult(billing);
    }

    /// <inheritdoc />
    public string GenerateStatementNo()
    {
        // 1. 账单号格式：BILL-{yyyyMMdd}-{序号}
        var date = DateTime.UtcNow.ToString("yyyyMMdd", CultureInfo.InvariantCulture);

        // 2. 使用雪花 ID 作为全局递增序号，确保分布式唯一
        var sequence = idGenerator.NextId();
        return $"BILL-{date}-{sequence}";
    }

    /// <inheritdoc />
    public async Task<int> ProcessOverdueBillingsAsync(CancellationToken cancellationToken = default)
    {
        var processedAt = DateTime.UtcNow;
        var currentTenantId = tenantContextAccessor.Current?.TenantId ?? 0;
        if (currentTenantId != 0)
        {
            return await ProcessOverdueBillingsSingleTenantAsync(processedAt, cancellationToken);
        }

        // 1. (空行后) 系统上下文：逐租户处理，避免跨租户写入
        var tenants = await tenantRepository.SearchAsync(null, null, cancellationToken);
        var targets = tenants.Where(x => x.Id > 0).ToList();
        if (targets.Count == 0)
        {
            return 0;
        }

        var totalUpdated = 0;
        foreach (var tenant in targets)
        {
            using (tenantContextAccessor.EnterTenantScope(tenant.Id, "billing:overdue", tenant.Code))
            {
                totalUpdated += await ProcessOverdueBillingsSingleTenantAsync(processedAt, cancellationToken);
            }
        }

        return totalUpdated;
    }

    private async Task<int> ProcessOverdueBillingsSingleTenantAsync(DateTime processedAt, CancellationToken cancellationToken)
    {
        // 1. 查询当前租户已超过到期日且仍处于待支付的账单（由仓储按 DueDate + Status 筛选）
        var overdueBillings = await billingRepository.GetOverdueBillingsAsync(cancellationToken);
        if (overdueBillings.Count == 0)
        {
            return 0;
        }

        // 2. (空行后) 批量标记逾期（防御性：再次判断 Pending）
        var updated = 0;
        foreach (var billing in overdueBillings)
        {
            if (billing.Status != TenantBillingStatus.Pending)
            {
                continue;
            }

            billing.MarkAsOverdue();
            billing.OverdueNotifiedAt ??= processedAt;
            billing.UpdatedAt = processedAt;

            await billingRepository.UpdateAsync(billing, cancellationToken);
            updated++;
        }

        // 3. (空行后) 持久化
        if (updated > 0)
        {
            await billingRepository.SaveChangesAsync(cancellationToken);
        }

        return updated;
    }

    /// <inheritdoc />
    public decimal CalculateTotalAmount(decimal baseAmount, decimal discountAmount, decimal taxAmount)
    {
        return baseAmount - discountAmount + taxAmount;
    }

    /// <inheritdoc />
    public bool CanProcessPayment(TenantBillingStatement billing)
    {
        ArgumentNullException.ThrowIfNull(billing);

        return billing.Status switch
        {
            TenantBillingStatus.Pending => true,
            TenantBillingStatus.Overdue => true,
            _ => false
        };
    }
}
