using MediatR;
using System.Data;
using System.Data.Common;
using TakeoutSaaS.Application.App.Tenants.Dto;
using TakeoutSaaS.Application.App.Tenants.Queries;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Data;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Tenants.Handlers;

/// <summary>
/// 分页查询租户配额使用历史处理器。
/// </summary>
public sealed class GetTenantQuotaUsageHistoryQueryHandler(
    ITenantRepository tenantRepository,
    ITenantProvider tenantProvider,
    IDapperExecutor dapperExecutor)
    : IRequestHandler<GetTenantQuotaUsageHistoryQuery, PagedResult<QuotaUsageHistoryDto>>
{
    /// <inheritdoc />
    public async Task<PagedResult<QuotaUsageHistoryDto>> Handle(GetTenantQuotaUsageHistoryQuery request, CancellationToken cancellationToken)
    {
        // 1. 校验租户上下文（租户端禁止跨租户）
        var currentTenantId = tenantProvider.GetCurrentTenantId();
        if (currentTenantId <= 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "缺少租户标识");
        }

        // 2. (空行后) 兼容旧调用：若传入 TenantId，则必须与当前租户一致
        if (request.TenantId > 0 && request.TenantId != currentTenantId)
        {
            throw new BusinessException(ErrorCodes.Forbidden, "无权查询其他租户配额历史");
        }

        // 3. (空行后) 校验租户存在
        _ = await tenantRepository.FindByIdAsync(currentTenantId, cancellationToken)
            ?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");

        // 4. (空行后) 规范化分页
        var page = request.Page <= 0 ? 1 : request.Page;
        var pageSize = request.PageSize is <= 0 or > 100 ? 10 : request.PageSize;
        var offset = (page - 1) * pageSize;

        // 5. (空行后) 查询总数 + 列表
        return await dapperExecutor.QueryAsync(
            DatabaseConstants.AppDataSource,
            DatabaseConnectionRole.Read,
            async (connection, token) =>
            {
                // 3.1 统计总数
                var total = await ExecuteScalarIntAsync(
                    connection,
                    BuildCountSql(),
                    [
                        ("tenantId", currentTenantId),
                        ("quotaType", request.QuotaType.HasValue ? (int)request.QuotaType.Value : null),
                        ("startDate", request.StartDate),
                        ("endDate", request.EndDate)
                    ],
                    token);

                // 3.2 查询列表
                await using var listCommand = CreateCommand(
                    connection,
                    BuildListSql(),
                    [
                        ("tenantId", currentTenantId),
                        ("quotaType", request.QuotaType.HasValue ? (int)request.QuotaType.Value : null),
                        ("startDate", request.StartDate),
                        ("endDate", request.EndDate),
                        ("offset", offset),
                        ("limit", pageSize)
                    ]);

                await using var reader = await listCommand.ExecuteReaderAsync(token);
                var items = new List<QuotaUsageHistoryDto>();
                while (await reader.ReadAsync(token))
                {
                    var quotaType = (TenantQuotaType)reader.GetInt32(0);
                    var usedValue = reader.GetDecimal(1);
                    var limitValue = reader.GetDecimal(2);
                    var recordedAt = reader.GetDateTime(3);
                    var changeType = (TenantQuotaUsageHistoryChangeType)reader.GetInt32(4);
                    decimal? changeAmount = reader.IsDBNull(5) ? null : reader.GetDecimal(5);
                    var changeReason = reader.IsDBNull(6) ? null : reader.GetString(6);

                    // 3.2.1 映射 DTO
                    items.Add(new QuotaUsageHistoryDto
                    {
                        QuotaType = quotaType,
                        UsedValue = usedValue,
                        LimitValue = limitValue,
                        RecordedAt = recordedAt,
                        ChangeType = MapChangeType(changeType),
                        ChangeAmount = changeAmount,
                        ChangeReason = changeReason
                    });
                }

                // 3.3 返回分页
                return new PagedResult<QuotaUsageHistoryDto>(items, page, pageSize, total);
            },
            cancellationToken);
    }

    private static string BuildCountSql()
    {
        return """
            select count(*)
            from public.tenant_quota_usage_histories h
            where h."DeletedAt" is null
              and h."TenantId" = @tenantId
              and (@quotaType::int is null or h."QuotaType" = @quotaType)
              and (@startDate::timestamp with time zone is null or h."RecordedAt" >= @startDate)
              and (@endDate::timestamp with time zone is null or h."RecordedAt" <= @endDate);
            """;
    }

    private static string BuildListSql()
    {
        return """
            select
              h."QuotaType",
              h."UsedValue",
              h."LimitValue",
              h."RecordedAt",
              h."ChangeType",
              h."ChangeAmount",
              h."ChangeReason"
            from public.tenant_quota_usage_histories h
            where h."DeletedAt" is null
              and h."TenantId" = @tenantId
              and (@quotaType::int is null or h."QuotaType" = @quotaType)
              and (@startDate::timestamp with time zone is null or h."RecordedAt" >= @startDate)
              and (@endDate::timestamp with time zone is null or h."RecordedAt" <= @endDate)
            order by h."RecordedAt" desc, h."Id" desc
            offset @offset
            limit @limit;
            """;
    }

    private static string MapChangeType(TenantQuotaUsageHistoryChangeType changeType)
    {
        return changeType switch
        {
            TenantQuotaUsageHistoryChangeType.Init => "init",
            TenantQuotaUsageHistoryChangeType.Snapshot => "snapshot",
            TenantQuotaUsageHistoryChangeType.Increase => "increase",
            TenantQuotaUsageHistoryChangeType.Decrease => "decrease",
            _ => "snapshot"
        };
    }

    private static async Task<int> ExecuteScalarIntAsync(
        IDbConnection connection,
        string sql,
        (string Name, object? Value)[] parameters,
        CancellationToken cancellationToken)
    {
        await using var command = CreateCommand(connection, sql, parameters);
        var result = await command.ExecuteScalarAsync(cancellationToken);
        return result is null or DBNull ? 0 : Convert.ToInt32(result);
    }

    private static DbCommand CreateCommand(IDbConnection connection, string sql, (string Name, object? Value)[] parameters)
    {
        var command = connection.CreateCommand();
        command.CommandText = sql;

        foreach (var (name, value) in parameters)
        {
            var p = command.CreateParameter();
            p.ParameterName = name;
            p.Value = value ?? DBNull.Value;
            command.Parameters.Add(p);
        }

        return (DbCommand)command;
    }
}
