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

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

/// <summary>
/// 财务概览映射与格式化。
/// </summary>
internal static class FinanceOverviewMapping
{
    /// <summary>
    /// 构建财务概览页面 DTO。
    /// </summary>
    public static FinanceOverviewDashboardDto ToDashboardDto(FinanceOverviewDashboardSnapshot snapshot)
    {
        // 1. 指标卡映射。
        var todayNetIncome = snapshot.Summary.TodayNetReceived - snapshot.Summary.TodayTotalCost;
        var yesterdayNetIncome = snapshot.Summary.YesterdayNetReceived - snapshot.Summary.YesterdayTotalCost;

        // 2. 近 30/7 天趋势映射。
        var incomeLast30 = snapshot.IncomeTrend
            .OrderBy(item => item.BusinessDate)
            .Select(item => new FinanceOverviewIncomeTrendPointDto
            {
                Date = item.BusinessDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
                DateLabel = item.BusinessDate.ToString("MM/dd", CultureInfo.InvariantCulture),
                Amount = RoundAmount(item.NetReceivedAmount)
            })
            .ToList();
        var incomeLast7 = incomeLast30.Skip(Math.Max(0, incomeLast30.Count - 7)).ToList();

        var profitLast30 = snapshot.ProfitTrend
            .OrderBy(item => item.BusinessDate)
            .Select(item => new FinanceOverviewProfitTrendPointDto
            {
                Date = item.BusinessDate.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
                DateLabel = item.BusinessDate.ToString("MM/dd", CultureInfo.InvariantCulture),
                RevenueAmount = RoundAmount(item.RevenueAmount),
                CostAmount = RoundAmount(item.CostAmount),
                NetProfitAmount = RoundAmount(item.NetProfitAmount)
            })
            .ToList();
        var profitLast7 = profitLast30.Skip(Math.Max(0, profitLast30.Count - 7)).ToList();

        // 3. 收入构成映射。
        var incomeTotal = RoundAmount(snapshot.IncomeComposition.Sum(item => item.Amount));
        var incomeItems = snapshot.IncomeComposition
            .OrderBy(item => GetChannelSort(item.Channel))
            .Select(item => new FinanceOverviewIncomeCompositionItemDto
            {
                Channel = ToChannelCode(item.Channel),
                ChannelText = ToChannelText(item.Channel),
                Amount = RoundAmount(item.Amount),
                Percentage = incomeTotal > 0
                    ? RoundAmount(item.Amount / incomeTotal * 100m)
                    : 0m
            })
            .ToList();

        // 4. 成本构成映射。
        var costTotal = RoundAmount(snapshot.CostComposition.Sum(item => item.Amount));
        var costItems = snapshot.CostComposition
            .OrderBy(item => GetCostCategorySort(item.CategoryCode))
            .Select(item => new FinanceOverviewCostCompositionItemDto
            {
                Category = item.CategoryCode,
                CategoryText = ToCostCategoryText(item.CategoryCode),
                Amount = RoundAmount(item.Amount),
                Percentage = costTotal > 0
                    ? RoundAmount(item.Amount / costTotal * 100m)
                    : 0m
            })
            .ToList();

        // 5. TOP10 映射。
        var topTotalRevenue = snapshot.TopProductTotalRevenue > 0
            ? snapshot.TopProductTotalRevenue
            : snapshot.TopProducts.Sum(item => item.RevenueAmount);
        var topItems = snapshot.TopProducts
            .OrderByDescending(item => item.RevenueAmount)
            .ThenByDescending(item => item.SalesQuantity)
            .Select((item, index) => new FinanceOverviewTopProductItemDto
            {
                Rank = index + 1,
                ProductName = item.ProductName,
                SalesQuantity = item.SalesQuantity,
                RevenueAmount = RoundAmount(item.RevenueAmount),
                Percentage = topTotalRevenue > 0
                    ? RoundAmount(item.RevenueAmount / topTotalRevenue * 100m)
                    : 0m
            })
            .ToList();

        return new FinanceOverviewDashboardDto
        {
            Dimension = ToDimensionCode(snapshot.Dimension),
            StoreId = snapshot.StoreId?.ToString(CultureInfo.InvariantCulture),
            TodayRevenue = BuildKpi(snapshot.Summary.TodayGrossRevenue, snapshot.Summary.YesterdayGrossRevenue, "较昨日"),
            ActualReceived = BuildKpi(snapshot.Summary.TodayNetReceived, snapshot.Summary.YesterdayNetReceived, "较昨日"),
            RefundAmount = BuildKpi(snapshot.Summary.TodayRefundAmount, snapshot.Summary.YesterdayRefundAmount, "较昨日"),
            NetIncome = BuildKpi(todayNetIncome, yesterdayNetIncome, "较昨日"),
            WithdrawableBalance = BuildKpi(
                snapshot.Summary.WithdrawableBalance,
                snapshot.Summary.WithdrawableBalanceLastWeek,
                "较上周"),
            IncomeTrend = new FinanceOverviewIncomeTrendDto
            {
                Last7Days = incomeLast7,
                Last30Days = incomeLast30
            },
            ProfitTrend = new FinanceOverviewProfitTrendDto
            {
                Last7Days = profitLast7,
                Last30Days = profitLast30
            },
            IncomeComposition = new FinanceOverviewIncomeCompositionDto
            {
                TotalAmount = incomeTotal,
                Items = incomeItems
            },
            CostComposition = new FinanceOverviewCostCompositionDto
            {
                TotalAmount = costTotal,
                Items = costItems
            },
            TopProducts = new FinanceOverviewTopProductDto
            {
                PeriodDays = 30,
                Items = topItems
            }
        };
    }

    private static FinanceOverviewKpiCardDto BuildKpi(decimal current, decimal previous, string compareLabel)
    {
        var normalizedCurrent = RoundAmount(current);
        var normalizedPrevious = RoundAmount(previous);
        return new FinanceOverviewKpiCardDto
        {
            Amount = normalizedCurrent,
            CompareAmount = normalizedPrevious,
            ChangeRate = CalculateChangeRate(normalizedCurrent, normalizedPrevious),
            Trend = ResolveTrend(normalizedCurrent, normalizedPrevious),
            CompareLabel = compareLabel
        };
    }

    private static decimal CalculateChangeRate(decimal current, decimal previous)
    {
        if (previous == 0m)
        {
            return current == 0m ? 0m : 100m;
        }

        var rate = (current - previous) / previous * 100m;
        return RoundAmount(rate);
    }

    private static string ResolveTrend(decimal current, decimal previous)
    {
        if (current > previous)
        {
            return "up";
        }

        if (current < previous)
        {
            return "down";
        }

        return "flat";
    }

    private static string ToDimensionCode(FinanceCostDimension value)
    {
        return value == FinanceCostDimension.Store ? "store" : "tenant";
    }

    private static string ToChannelCode(DeliveryType value)
    {
        return value switch
        {
            DeliveryType.Delivery => "delivery",
            DeliveryType.Pickup => "pickup",
            DeliveryType.DineIn => "dine_in",
            _ => "delivery"
        };
    }

    private static string ToChannelText(DeliveryType value)
    {
        return value switch
        {
            DeliveryType.Delivery => "外卖",
            DeliveryType.Pickup => "自提",
            DeliveryType.DineIn => "堂食",
            _ => "外卖"
        };
    }

    private static int GetChannelSort(DeliveryType value)
    {
        return value switch
        {
            DeliveryType.Delivery => 0,
            DeliveryType.Pickup => 1,
            DeliveryType.DineIn => 2,
            _ => 9
        };
    }

    private static int GetCostCategorySort(string? value)
    {
        return (value ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "food" => 0,
            "labor" => 1,
            "fixed" => 2,
            "packaging" => 3,
            "platform" => 4,
            _ => 9
        };
    }

    private static string ToCostCategoryText(string? value)
    {
        return (value ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "food" => "食材",
            "labor" => "人工",
            "fixed" => "固定",
            "packaging" => "包装",
            "platform" => "平台",
            _ => "其他"
        };
    }

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