using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;

namespace TakeoutSaaS.Domain.Tenants.Repositories;

/// <summary>
/// 租户账单仓储。
/// </summary>
public interface ITenantBillingRepository
{
    /// <summary>
    /// 查询账单列表，按状态与时间范围筛选。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="status">账单状态。</param>
    /// <param name="from">开始时间（UTC）。</param>
    /// <param name="to">结束时间（UTC）。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>账单集合。</returns>
    Task<IReadOnlyList<TenantBillingStatement>> SearchAsync(
        long tenantId,
        TenantBillingStatus? status,
        DateTime? from,
        DateTime? to,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// 按 ID 获取账单。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="billingId">账单 ID。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>账单实体或 null。</returns>
    Task<TenantBillingStatement?> FindByIdAsync(long tenantId, long billingId, CancellationToken cancellationToken = default);

    /// <summary>
    /// 按账单编号获取账单。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="statementNo">账单编号。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>账单实体或 null。</returns>
    Task<TenantBillingStatement?> FindByStatementNoAsync(long tenantId, string statementNo, CancellationToken cancellationToken = default);

    /// <summary>
    /// 按账单编号获取账单（不限租户，系统任务使用）。
    /// </summary>
    /// <param name="statementNo">账单编号。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>账单实体或 null。</returns>
    Task<TenantBillingStatement?> GetByStatementNoAsync(string statementNo, CancellationToken cancellationToken = default);

    /// <summary>
    /// 判断是否已存在指定周期开始时间的未取消账单（用于自动续费幂等）。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="periodStart">账单周期开始时间（UTC）。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>存在返回 true，否则 false。</returns>
    Task<bool> ExistsNotCancelledByPeriodStartAsync(
        long tenantId,
        DateTime periodStart,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// 获取逾期账单列表（已过到期日且未支付）。
    /// </summary>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>逾期账单集合。</returns>
    Task<IReadOnlyList<TenantBillingStatement>> GetOverdueBillingsAsync(CancellationToken cancellationToken = default);

    /// <summary>
    /// 获取即将到期的账单列表（未来 N 天内到期且未支付）。
    /// </summary>
    /// <param name="daysAhead">提前天数。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>即将到期的账单集合。</returns>
    Task<IReadOnlyList<TenantBillingStatement>> GetBillingsDueSoonAsync(int daysAhead, CancellationToken cancellationToken = default);

    /// <summary>
    /// 按租户 ID 获取账单列表。
    /// </summary>
    /// <param name="tenantId">租户 ID。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>账单集合。</returns>
    Task<IReadOnlyList<TenantBillingStatement>> GetByTenantIdAsync(long tenantId, CancellationToken cancellationToken = default);

    /// <summary>
    /// 按 ID 列表批量获取账单（系统任务/批量操作场景）。
    /// </summary>
    /// <param name="billingIds">账单 ID 列表。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>账单实体列表。</returns>
    Task<IReadOnlyList<TenantBillingStatement>> GetByIdsAsync(
        IReadOnlyCollection<long> billingIds,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// 新增账单。
    /// </summary>
    /// <param name="bill">账单实体。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    Task AddAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default);

    /// <summary>
    /// 更新账单。
    /// </summary>
    /// <param name="bill">账单实体。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    Task UpdateAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default);

    /// <summary>
    /// 保存变更。
    /// </summary>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    Task SaveChangesAsync(CancellationToken cancellationToken = default);

    /// <summary>
    /// 系统任务分页查询账单列表（跨租户）。
    /// </summary>
    /// <param name="tenantId">租户 ID 筛选（可选）。</param>
    /// <param name="status">账单状态筛选（可选）。</param>
    /// <param name="from">开始时间（UTC，可选）。</param>
    /// <param name="to">结束时间（UTC，可选）。</param>
    /// <param name="minAmount">最小应付金额筛选（包含，可选）。</param>
    /// <param name="maxAmount">最大应付金额筛选（包含，可选）。</param>
    /// <param name="keyword">关键词搜索（账单号或租户名）。</param>
    /// <param name="pageNumber">页码（从 1 开始）。</param>
    /// <param name="pageSize">页大小。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>账单集合与总数。</returns>
    Task<(IReadOnlyList<TenantBillingStatement> Items, int Total)> SearchPagedAsync(
        long? tenantId,
        TenantBillingStatus? status,
        DateTime? from,
        DateTime? to,
        decimal? minAmount,
        decimal? maxAmount,
        string? keyword,
        int pageNumber,
        int pageSize,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// 获取账单统计数据（用于报表与仪表盘）。
    /// </summary>
    /// <param name="tenantId">租户 ID（可选，系统任务可跨租户统计）。</param>
    /// <param name="startDate">统计开始时间（UTC）。</param>
    /// <param name="endDate">统计结束时间（UTC）。</param>
    /// <param name="groupBy">分组方式（Day/Week/Month）。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>统计结果。</returns>
    Task<TenantBillingStatistics> GetStatisticsAsync(
        long? tenantId,
        DateTime startDate,
        DateTime endDate,
        string groupBy,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// 按 ID 获取账单（不限租户，系统任务使用）。
    /// </summary>
    /// <param name="billingId">账单 ID。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>账单实体或 null。</returns>
    Task<TenantBillingStatement?> FindByIdAsync(long billingId, CancellationToken cancellationToken = default);
}

/// <summary>
/// 账单统计结果。
/// </summary>
public sealed record TenantBillingStatistics
{
    /// <summary>
    /// 总账单金额（统计区间内）。
    /// </summary>
    public decimal TotalAmount { get; init; }

    /// <summary>
    /// 已支付金额（统计区间内）。
    /// </summary>
    public decimal PaidAmount { get; init; }

    /// <summary>
    /// 未支付金额（统计区间内）。
    /// </summary>
    public decimal UnpaidAmount { get; init; }

    /// <summary>
    /// 逾期金额（统计区间内）。
    /// </summary>
    public decimal OverdueAmount { get; init; }

    /// <summary>
    /// 总账单数量（统计区间内）。
    /// </summary>
    public int TotalCount { get; init; }

    /// <summary>
    /// 已支付账单数量（统计区间内）。
    /// </summary>
    public int PaidCount { get; init; }

    /// <summary>
    /// 未支付账单数量（统计区间内）。
    /// </summary>
    public int UnpaidCount { get; init; }

    /// <summary>
    /// 逾期账单数量（统计区间内）。
    /// </summary>
    public int OverdueCount { get; init; }

    /// <summary>
    /// 趋势数据（按 groupBy 聚合）。
    /// </summary>
    public IReadOnlyList<TenantBillingTrendDataPoint> TrendData { get; init; } = [];
}

/// <summary>
/// 账单趋势统计点。
/// </summary>
public sealed record TenantBillingTrendDataPoint
{
    /// <summary>
    /// 分组时间点（Day/Week/Month 的代表日期，UTC）。
    /// </summary>
    public DateTime Period { get; init; }

    /// <summary>
    /// 账单数量。
    /// </summary>
    public int Count { get; init; }

    /// <summary>
    /// 总金额。
    /// </summary>
    public decimal TotalAmount { get; init; }

    /// <summary>
    /// 已支付金额。
    /// </summary>
    public decimal PaidAmount { get; init; }
}
