using System.Globalization;
using System.Text;
using MediatR;
using TakeoutSaaS.Application.App.Customers.Dto;
using TakeoutSaaS.Application.App.Customers.Queries;
using TakeoutSaaS.Domain.Orders.Repositories;
using TakeoutSaaS.Shared.Abstractions.Data;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Customers.Handlers;

/// <summary>
/// 客户分析报表导出处理器。
/// </summary>
public sealed class ExportCustomerAnalysisCsvQueryHandler(
    IOrderRepository orderRepository,
    IDapperExecutor dapperExecutor,
    ITenantProvider tenantProvider)
    : IRequestHandler<ExportCustomerAnalysisCsvQuery, CustomerExportDto>
{
    /// <inheritdoc />
    public async Task<CustomerExportDto> Handle(
        ExportCustomerAnalysisCsvQuery request,
        CancellationToken cancellationToken)
    {
        var periodCode = string.IsNullOrWhiteSpace(request.PeriodCode)
            ? "30d"
            : request.PeriodCode.Trim().ToLowerInvariant();
        var periodDays = Math.Clamp(request.PeriodDays, 7, 365);

        if (request.VisibleStoreIds.Count == 0)
        {
            return BuildExport(
                BuildCsv(periodCode, periodDays, [], DateTime.UtcNow),
                0);
        }

        var customers = await CustomerAnalyticsSupport.LoadCustomersAsync(
            orderRepository,
            dapperExecutor,
            tenantProvider,
            request.VisibleStoreIds,
            cancellationToken);

        return BuildExport(
            BuildCsv(periodCode, periodDays, customers, DateTime.UtcNow),
            Math.Min(10, customers.Count));
    }

    private static CustomerExportDto BuildExport(string csv, int totalCount)
    {
        var bytes = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(csv)).ToArray();
        return new CustomerExportDto
        {
            FileName = $"客户分析报表_{DateTime.UtcNow:yyyyMMddHHmmss}.csv",
            FileContentBase64 = Convert.ToBase64String(bytes),
            TotalCount = totalCount
        };
    }

    private static string BuildCsv(
        string periodCode,
        int periodDays,
        IReadOnlyList<CustomerAggregate> customers,
        DateTime nowUtc)
    {
        var currentStart = nowUtc.AddDays(-periodDays);
        var previousStart = currentStart.AddDays(-periodDays);

        var totalCustomers = customers.Count;
        var newCustomers = customers.Count(item => item.RegisteredAt >= currentStart);
        var previousNewCustomers = customers.Count(item =>
            item.RegisteredAt >= previousStart &&
            item.RegisteredAt < currentStart);
        var activeCustomers = customers.Count(item => item.LastOrderAt >= currentStart);
        var averageLifetimeValue = totalCustomers <= 0
            ? 0
            : decimal.Round(
                customers.Sum(item => item.TotalAmount) / totalCustomers,
                2,
                MidpointRounding.AwayFromZero);

        var composition = CustomerAnalysisSegmentSupport.BuildComposition(customers, nowUtc, periodDays);
        var amountDistribution = CustomerAnalysisSegmentSupport.BuildAmountDistribution(customers);
        var rfmRows = CustomerAnalysisSegmentSupport.BuildRfmRows(customers, nowUtc);
        var topCustomers = CustomerAnalysisSegmentSupport.BuildTopCustomers(customers, 10);

        var sb = new StringBuilder();
        sb.AppendLine($"统计周期,{Escape(ResolvePeriodLabel(periodCode, periodDays))}");
        sb.AppendLine();

        sb.AppendLine("核心指标");
        sb.AppendLine("指标,值");
        sb.AppendLine($"客户总数,{totalCustomers}");
        sb.AppendLine($"周期新增,{newCustomers}");
        sb.AppendLine($"新增增长率,{CustomerAnalyticsSupport.ToGrowthRatePercent(newCustomers, previousNewCustomers):0.#}%");
        sb.AppendLine($"周期活跃客户,{activeCustomers}");
        sb.AppendLine($"活跃率,{CustomerAnalyticsSupport.ToRatePercent(activeCustomers, totalCustomers):0.#}%");
        sb.AppendLine($"平均客户价值,{averageLifetimeValue.ToString("0.00", CultureInfo.InvariantCulture)}");
        sb.AppendLine();

        sb.AppendLine("新老客占比");
        sb.AppendLine("分层,人数,占比");
        foreach (var item in composition)
        {
            sb.AppendLine($"{Escape(item.Label)},{item.Count},{item.Percent:0.#}%");
        }
        sb.AppendLine();

        sb.AppendLine("客单价分布");
        sb.AppendLine("区间,人数,占比");
        foreach (var item in amountDistribution)
        {
            sb.AppendLine($"{Escape(item.Label)},{item.Count},{item.Percent:0.#}%");
        }
        sb.AppendLine();

        sb.AppendLine("RFM客户分层");
        sb.AppendLine("活跃度,分群,标签,人数");
        for (var rowIndex = 0; rowIndex < rfmRows.Count; rowIndex += 1)
        {
            var row = rfmRows[rowIndex];
            for (var columnIndex = 0; columnIndex < row.Cells.Count; columnIndex += 1)
            {
                var cell = row.Cells[columnIndex];
                sb.AppendLine($"{Escape(row.Label)},{Escape(ResolveRfmColumnLabel(columnIndex))},{Escape(cell.Label)},{cell.Count}");
            }
        }
        sb.AppendLine();

        sb.AppendLine("高价值客户TOP10");
        sb.AppendLine("排名,客户,手机号,累计消费,下单次数,客单价,最近下单,标签");
        foreach (var item in topCustomers)
        {
            var tags = item.Tags.Count == 0
                ? string.Empty
                : string.Join('、', item.Tags.Select(tag => tag.Label));

            sb.AppendLine(string.Join(',',
            [
                item.Rank.ToString(CultureInfo.InvariantCulture),
                Escape(item.Name),
                Escape(item.PhoneMasked),
                Escape(item.TotalAmount.ToString("0.00", CultureInfo.InvariantCulture)),
                Escape(item.OrderCount.ToString(CultureInfo.InvariantCulture)),
                Escape(item.AverageAmount.ToString("0.00", CultureInfo.InvariantCulture)),
                Escape(item.LastOrderAt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)),
                Escape(tags)
            ]));
        }

        return sb.ToString();
    }

    private static string ResolvePeriodLabel(string periodCode, int periodDays)
    {
        return periodCode switch
        {
            "7d" => "近7天",
            "30d" => "近30天",
            "90d" => "近90天",
            "365d" => "近1年",
            _ => $"近{periodDays}天"
        };
    }

    private static string ResolveRfmColumnLabel(int columnIndex)
    {
        return columnIndex switch
        {
            0 => "高频高额",
            1 => "高频低额",
            2 => "低频高额",
            _ => "低频低额"
        };
    }

    private static string Escape(string input)
    {
        if (!input.Contains('"') && !input.Contains(',') && !input.Contains('\n') && !input.Contains('\r'))
        {
            return input;
        }

        return $"\"{input.Replace("\"", "\"\"")}\"";
    }
}
