using System.Globalization;
using TakeoutSaaS.Application.App.Finance.Reports.Dto;
using TakeoutSaaS.Domain.Finance.Enums;
using TakeoutSaaS.Domain.Finance.Models;

namespace TakeoutSaaS.Application.App.Finance.Reports.Handlers;

/// <summary>
/// 经营报表映射工具。
/// </summary>
internal static class FinanceBusinessReportMapping
{
    /// <summary>
    /// 映射列表行 DTO。
    /// </summary>
    public static FinanceBusinessReportListItemDto ToListItem(FinanceBusinessReportListItemSnapshot source)
    {
        return new FinanceBusinessReportListItemDto
        {
            ReportId = source.ReportId.ToString(CultureInfo.InvariantCulture),
            DateText = FormatPeriodText(source.PeriodType, source.PeriodStartAt, source.PeriodEndAt),
            RevenueAmount = RoundMoney(source.RevenueAmount),
            OrderCount = Math.Max(0, source.OrderCount),
            AverageOrderValue = RoundMoney(source.AverageOrderValue),
            RefundRatePercent = RoundPercent(source.RefundRate),
            CostTotalAmount = RoundMoney(source.CostTotalAmount),
            NetProfitAmount = RoundMoney(source.NetProfitAmount),
            ProfitRatePercent = RoundPercent(source.ProfitRate),
            Status = ToStatusCode(source.Status),
            StatusText = ToStatusText(source.Status),
            CanDownload = source.Status == FinanceBusinessReportStatus.Succeeded
        };
    }

    /// <summary>
    /// 映射详情 DTO。
    /// </summary>
    public static FinanceBusinessReportDetailDto ToDetail(FinanceBusinessReportDetailSnapshot source)
    {
        return new FinanceBusinessReportDetailDto
        {
            ReportId = source.ReportId.ToString(CultureInfo.InvariantCulture),
            Title = BuildTitle(source.PeriodType, source.PeriodStartAt, source.PeriodEndAt),
            PeriodType = ToPeriodTypeCode(source.PeriodType),
            Status = ToStatusCode(source.Status),
            StatusText = ToStatusText(source.Status),
            Kpis = source.Kpis.Select(ToKpi).ToList(),
            IncomeBreakdowns = source.IncomeBreakdowns.Select(ToBreakdown).ToList(),
            CostBreakdowns = source.CostBreakdowns.Select(ToBreakdown).ToList()
        };
    }

    /// <summary>
    /// 周期类型编码。
    /// </summary>
    public static string ToPeriodTypeCode(FinanceBusinessReportPeriodType value)
    {
        return value switch
        {
            FinanceBusinessReportPeriodType.Daily => "daily",
            FinanceBusinessReportPeriodType.Weekly => "weekly",
            FinanceBusinessReportPeriodType.Monthly => "monthly",
            _ => "daily"
        };
    }

    private static FinanceBusinessReportKpiDto ToKpi(FinanceBusinessReportKpiSnapshot source)
    {
        return new FinanceBusinessReportKpiDto
        {
            Key = source.Key,
            Label = source.Label,
            ValueText = FormatKpiValue(source.Key, source.Value),
            YoyChangeRate = RoundRate(source.YoyChangeRate),
            MomChangeRate = RoundRate(source.MomChangeRate)
        };
    }

    private static FinanceBusinessReportBreakdownItemDto ToBreakdown(FinanceBusinessReportBreakdownSnapshot source)
    {
        return new FinanceBusinessReportBreakdownItemDto
        {
            Key = source.Key,
            Label = source.Label,
            Amount = RoundMoney(source.Amount),
            RatioPercent = RoundPercent(source.Ratio)
        };
    }

    private static string FormatPeriodText(
        FinanceBusinessReportPeriodType periodType,
        DateTime startAt,
        DateTime endAt)
    {
        return periodType switch
        {
            FinanceBusinessReportPeriodType.Daily => startAt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
            FinanceBusinessReportPeriodType.Weekly =>
                $"{startAt:MM-dd} ~ {endAt.AddDays(-1):MM-dd}",
            FinanceBusinessReportPeriodType.Monthly => startAt.ToString("yyyy年M月", CultureInfo.InvariantCulture),
            _ => startAt.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)
        };
    }

    private static string BuildTitle(
        FinanceBusinessReportPeriodType periodType,
        DateTime startAt,
        DateTime endAt)
    {
        return periodType switch
        {
            FinanceBusinessReportPeriodType.Daily => $"{startAt:yyyy年M月d日} 经营日报",
            FinanceBusinessReportPeriodType.Weekly => $"{startAt:yyyy年M月d日}~{endAt.AddDays(-1):M月d日} 经营周报",
            FinanceBusinessReportPeriodType.Monthly => $"{startAt:yyyy年M月} 经营月报",
            _ => "经营报表"
        };
    }

    private static string ToStatusCode(FinanceBusinessReportStatus status)
    {
        return status switch
        {
            FinanceBusinessReportStatus.Queued => "queued",
            FinanceBusinessReportStatus.Running => "running",
            FinanceBusinessReportStatus.Succeeded => "succeeded",
            FinanceBusinessReportStatus.Failed => "failed",
            _ => "queued"
        };
    }

    private static string ToStatusText(FinanceBusinessReportStatus status)
    {
        return status switch
        {
            FinanceBusinessReportStatus.Queued => "排队中",
            FinanceBusinessReportStatus.Running => "生成中",
            FinanceBusinessReportStatus.Succeeded => "已生成",
            FinanceBusinessReportStatus.Failed => "生成失败",
            _ => "排队中"
        };
    }

    private static string FormatKpiValue(string key, decimal value)
    {
        if (key is "order_count")
        {
            return Math.Round(value, 0, MidpointRounding.AwayFromZero).ToString("0", CultureInfo.InvariantCulture);
        }

        if (key is "refund_rate" or "profit_rate")
        {
            return $"{RoundPercent(value):0.##}%";
        }

        if (key is "average_order_value")
        {
            return $"¥{RoundMoney(value):0.##}";
        }

        return $"¥{RoundMoney(value):0.##}";
    }

    private static decimal RoundMoney(decimal value)
    {
        return decimal.Round(value, 2, MidpointRounding.AwayFromZero);
    }

    private static decimal RoundPercent(decimal value)
    {
        return decimal.Round(value * 100m, 2, MidpointRounding.AwayFromZero);
    }

    private static decimal RoundRate(decimal value)
    {
        return decimal.Round(value, 2, MidpointRounding.AwayFromZero);
    }
}

