using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Billings.Entities;
using TakeoutSaaS.Domain.Billings.Enums;
using TakeoutSaaS.Domain.Billings.Repositories;
using TakeoutSaaS.Infrastructure.App.Persistence;

namespace TakeoutSaaS.Infrastructure.App.Repositories;

/// <summary>
/// 账单仓储实现（AdminApi 使用）。
/// </summary>
public sealed class EfBillingRepository(TakeoutAdminDbContext context) : IBillingRepository
{
    /// <inheritdoc />
    public async Task<(IReadOnlyList<BillingListResult> Items, int TotalCount)> GetListAsync(
        long? tenantId,
        TenantBillingStatus? status,
        TenantBillingType? billingType,
        DateTime? startDate,
        DateTime? endDate,
        decimal? minAmount,
        decimal? maxAmount,
        string? keyword,
        string? sortBy,
        bool? sortDesc,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        // 1. 构建基础查询
        var query = from b in context.TenantBillingStatements.AsNoTracking()
                    join t in context.Tenants.AsNoTracking() on b.TenantId equals t.Id into tenants
                    from t in tenants.DefaultIfEmpty()
                    where b.DeletedAt == null
                    select new { Billing = b, TenantName = t != null ? t.Name : (string?)null };

        // 2. 应用租户 ID 过滤
        if (tenantId.HasValue)
        {
            query = query.Where(x => x.Billing.TenantId == tenantId.Value);
        }

        // 3. 应用状态过滤
        if (status.HasValue)
        {
            query = query.Where(x => x.Billing.Status == status.Value);
        }

        // 4. 应用账单类型过滤
        if (billingType.HasValue)
        {
            query = query.Where(x => x.Billing.BillingType == billingType.Value);
        }

        // 5. 应用日期范围过滤
        if (startDate.HasValue)
        {
            query = query.Where(x => x.Billing.CreatedAt >= startDate.Value);
        }

        if (endDate.HasValue)
        {
            query = query.Where(x => x.Billing.CreatedAt <= endDate.Value);
        }

        // 6. 应用金额范围过滤
        if (minAmount.HasValue)
        {
            query = query.Where(x => x.Billing.AmountDue >= minAmount.Value);
        }

        if (maxAmount.HasValue)
        {
            query = query.Where(x => x.Billing.AmountDue <= maxAmount.Value);
        }

        // 7. 应用关键词过滤
        if (!string.IsNullOrWhiteSpace(keyword))
        {
            var normalized = keyword.Trim();
            query = query.Where(x => x.Billing.StatementNo.Contains(normalized) ||
                                    (x.TenantName != null && x.TenantName.Contains(normalized)));
        }

        // 8. 获取总数
        var totalCount = await query.CountAsync(cancellationToken);

        // 9. 应用排序并分页查询
        var isDesc = sortDesc ?? true;
        var sortField = sortBy?.ToLowerInvariant() ?? "createdat";

        List<BillingListResult> items;

        if (sortField == "duedate")
        {
            items = isDesc
                ? await query.OrderByDescending(x => x.Billing.DueDate).ThenBy(x => x.Billing.Id)
                    .Skip((page - 1) * pageSize).Take(pageSize)
                    .Select(x => new BillingListResult(
                        x.Billing.Id, x.Billing.TenantId, x.TenantName, x.Billing.StatementNo,
                        x.Billing.BillingType, x.Billing.PeriodStart, x.Billing.PeriodEnd,
                        x.Billing.AmountDue, x.Billing.AmountPaid, x.Billing.DiscountAmount,
                        x.Billing.TaxAmount, x.Billing.Currency, x.Billing.Status, x.Billing.DueDate,
                        x.Billing.OverdueNotifiedAt, x.Billing.CreatedAt, x.Billing.UpdatedAt))
                    .ToListAsync(cancellationToken)
                : await query.OrderBy(x => x.Billing.DueDate).ThenBy(x => x.Billing.Id)
                    .Skip((page - 1) * pageSize).Take(pageSize)
                    .Select(x => new BillingListResult(
                        x.Billing.Id, x.Billing.TenantId, x.TenantName, x.Billing.StatementNo,
                        x.Billing.BillingType, x.Billing.PeriodStart, x.Billing.PeriodEnd,
                        x.Billing.AmountDue, x.Billing.AmountPaid, x.Billing.DiscountAmount,
                        x.Billing.TaxAmount, x.Billing.Currency, x.Billing.Status, x.Billing.DueDate,
                        x.Billing.OverdueNotifiedAt, x.Billing.CreatedAt, x.Billing.UpdatedAt))
                    .ToListAsync(cancellationToken);
        }
        else if (sortField == "amountdue")
        {
            items = isDesc
                ? await query.OrderByDescending(x => x.Billing.AmountDue).ThenBy(x => x.Billing.Id)
                    .Skip((page - 1) * pageSize).Take(pageSize)
                    .Select(x => new BillingListResult(
                        x.Billing.Id, x.Billing.TenantId, x.TenantName, x.Billing.StatementNo,
                        x.Billing.BillingType, x.Billing.PeriodStart, x.Billing.PeriodEnd,
                        x.Billing.AmountDue, x.Billing.AmountPaid, x.Billing.DiscountAmount,
                        x.Billing.TaxAmount, x.Billing.Currency, x.Billing.Status, x.Billing.DueDate,
                        x.Billing.OverdueNotifiedAt, x.Billing.CreatedAt, x.Billing.UpdatedAt))
                    .ToListAsync(cancellationToken)
                : await query.OrderBy(x => x.Billing.AmountDue).ThenBy(x => x.Billing.Id)
                    .Skip((page - 1) * pageSize).Take(pageSize)
                    .Select(x => new BillingListResult(
                        x.Billing.Id, x.Billing.TenantId, x.TenantName, x.Billing.StatementNo,
                        x.Billing.BillingType, x.Billing.PeriodStart, x.Billing.PeriodEnd,
                        x.Billing.AmountDue, x.Billing.AmountPaid, x.Billing.DiscountAmount,
                        x.Billing.TaxAmount, x.Billing.Currency, x.Billing.Status, x.Billing.DueDate,
                        x.Billing.OverdueNotifiedAt, x.Billing.CreatedAt, x.Billing.UpdatedAt))
                    .ToListAsync(cancellationToken);
        }
        else
        {
            items = isDesc
                ? await query.OrderByDescending(x => x.Billing.CreatedAt).ThenBy(x => x.Billing.Id)
                    .Skip((page - 1) * pageSize).Take(pageSize)
                    .Select(x => new BillingListResult(
                        x.Billing.Id, x.Billing.TenantId, x.TenantName, x.Billing.StatementNo,
                        x.Billing.BillingType, x.Billing.PeriodStart, x.Billing.PeriodEnd,
                        x.Billing.AmountDue, x.Billing.AmountPaid, x.Billing.DiscountAmount,
                        x.Billing.TaxAmount, x.Billing.Currency, x.Billing.Status, x.Billing.DueDate,
                        x.Billing.OverdueNotifiedAt, x.Billing.CreatedAt, x.Billing.UpdatedAt))
                    .ToListAsync(cancellationToken)
                : await query.OrderBy(x => x.Billing.CreatedAt).ThenBy(x => x.Billing.Id)
                    .Skip((page - 1) * pageSize).Take(pageSize)
                    .Select(x => new BillingListResult(
                        x.Billing.Id, x.Billing.TenantId, x.TenantName, x.Billing.StatementNo,
                        x.Billing.BillingType, x.Billing.PeriodStart, x.Billing.PeriodEnd,
                        x.Billing.AmountDue, x.Billing.AmountPaid, x.Billing.DiscountAmount,
                        x.Billing.TaxAmount, x.Billing.Currency, x.Billing.Status, x.Billing.DueDate,
                        x.Billing.OverdueNotifiedAt, x.Billing.CreatedAt, x.Billing.UpdatedAt))
                    .ToListAsync(cancellationToken);
        }

        // 10. 返回结果
        return (items, totalCount);
    }

    /// <inheritdoc />
    public async Task<TenantBillingStatement?> GetByIdAsync(long billingId, CancellationToken cancellationToken = default)
    {
        // 1. 查询账单（排除已删除，无跟踪）
        return await context.TenantBillingStatements
            .AsNoTracking()
            .Where(b => b.Id == billingId && b.DeletedAt == null)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<TenantBillingStatement?> GetByIdForUpdateAsync(long billingId, CancellationToken cancellationToken = default)
    {
        // 1. 查询账单（排除已删除，带跟踪用于更新）
        return await context.TenantBillingStatements
            .Where(b => b.Id == billingId && b.DeletedAt == null)
            .FirstOrDefaultAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task<BillingDetailResult?> GetDetailAsync(long billingId, CancellationToken cancellationToken = default)
    {
        // 1. 查询账单详情（带租户信息）
        var result = await (from b in context.TenantBillingStatements.AsNoTracking()
                          join t in context.Tenants.AsNoTracking() on b.TenantId equals t.Id into tenants
                          from t in tenants.DefaultIfEmpty()
                          where b.Id == billingId && b.DeletedAt == null
                          select new
                          {
                              Billing = b,
                              TenantName = t != null ? t.Name : (string?)null
                          }).FirstOrDefaultAsync(cancellationToken);

        // 2. 如果不存在，返回 null
        if (result is null)
        {
            return null;
        }

        // 3. 返回详情结果
        return new BillingDetailResult(
            result.Billing.Id,
            result.Billing.TenantId,
            result.TenantName,
            result.Billing.StatementNo,
            result.Billing.BillingType,
            result.Billing.PeriodStart,
            result.Billing.PeriodEnd,
            result.Billing.AmountDue,
            result.Billing.AmountPaid,
            result.Billing.DiscountAmount,
            result.Billing.TaxAmount,
            result.Billing.Currency,
            result.Billing.Status,
            result.Billing.DueDate,
            result.Billing.LineItemsJson,
            result.Billing.Notes,
            result.Billing.OverdueNotifiedAt,
            result.Billing.ReminderSentAt,
            result.Billing.CreatedAt,
            result.Billing.UpdatedAt);
    }

    /// <inheritdoc />
    public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        // 1. 保存变更
        await context.SaveChangesAsync(cancellationToken);
    }

    /// <inheritdoc />
    public async Task AddPaymentAsync(TenantPayment payment, CancellationToken cancellationToken = default)
    {
        // 1. 添加支付记录
        await context.TenantPayments.AddAsync(payment, cancellationToken);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<TenantPayment>> GetPaymentsByBillingIdAsync(long billingStatementId, CancellationToken cancellationToken = default)
    {
        // 1. 查询账单的支付记录（排除已删除，按创建时间排序）
        return await context.TenantPayments
            .AsNoTracking()
            .Where(p => p.BillingStatementId == billingStatementId && p.DeletedAt == null)
            .OrderBy(p => p.CreatedAt)
            .ToListAsync(cancellationToken);
    }
}
