using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TakeoutSaaS.Application.App.Finance.Overview.Dto;
using TakeoutSaaS.Application.App.Finance.Overview.Queries;
using TakeoutSaaS.Application.App.Stores.Services;
using TakeoutSaaS.Domain.Finance.Enums;
using TakeoutSaaS.Infrastructure.App.Persistence;
using TakeoutSaaS.Module.Authorization.Attributes;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Results;
using TakeoutSaaS.Shared.Web.Api;
using TakeoutSaaS.TenantApi.Contracts.Finance;

namespace TakeoutSaaS.TenantApi.Controllers;

/// <summary>
/// 财务中心概览驾驶舱。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/tenant/v{version:apiVersion}/finance/overview")]
public sealed class FinanceOverviewController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService) : BaseApiController
{
    private const string ViewPermission = "tenant:finance:overview:view";

    /// <summary>
    /// 查询财务概览驾驶舱数据。
    /// </summary>
    [HttpGet("dashboard")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceOverviewDashboardResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceOverviewDashboardResponse>> Dashboard(
        [FromQuery] FinanceOverviewDashboardRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析维度与作用域。
        var dimension = ParseDimension(request.Dimension);
        long? storeId = null;
        if (dimension == FinanceCostDimension.Store)
        {
            storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
            await EnsureStoreAccessibleAsync(storeId.Value, cancellationToken);
        }

        // 2. 查询概览数据。
        var dashboard = await mediator.Send(new GetFinanceOverviewDashboardQuery
        {
            Dimension = dimension,
            StoreId = storeId,
            CurrentUtc = DateTime.UtcNow
        }, cancellationToken);

        // 3. 映射响应并返回。
        return ApiResponse<FinanceOverviewDashboardResponse>.Ok(MapDashboard(dashboard));
    }

    private async Task EnsureStoreAccessibleAsync(long storeId, CancellationToken cancellationToken)
    {
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);
        await StoreApiHelpers.EnsureStoreAccessibleAsync(dbContext, tenantId, merchantId, storeId, cancellationToken);
    }

    private static FinanceCostDimension ParseDimension(string? value)
    {
        return (value ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "" or "tenant" => FinanceCostDimension.Tenant,
            "store" => FinanceCostDimension.Store,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "dimension 非法")
        };
    }

    private static FinanceOverviewDashboardResponse MapDashboard(FinanceOverviewDashboardDto source)
    {
        return new FinanceOverviewDashboardResponse
        {
            Dimension = source.Dimension,
            StoreId = source.StoreId,
            TodayRevenue = MapKpi(source.TodayRevenue),
            ActualReceived = MapKpi(source.ActualReceived),
            RefundAmount = MapKpi(source.RefundAmount),
            NetIncome = MapKpi(source.NetIncome),
            WithdrawableBalance = MapKpi(source.WithdrawableBalance),
            IncomeTrend = new FinanceOverviewIncomeTrendResponse
            {
                Last7Days = source.IncomeTrend.Last7Days.Select(item => new FinanceOverviewIncomeTrendPointResponse
                {
                    Date = item.Date,
                    DateLabel = item.DateLabel,
                    Amount = item.Amount
                }).ToList(),
                Last30Days = source.IncomeTrend.Last30Days.Select(item => new FinanceOverviewIncomeTrendPointResponse
                {
                    Date = item.Date,
                    DateLabel = item.DateLabel,
                    Amount = item.Amount
                }).ToList()
            },
            ProfitTrend = new FinanceOverviewProfitTrendResponse
            {
                Last7Days = source.ProfitTrend.Last7Days.Select(item => new FinanceOverviewProfitTrendPointResponse
                {
                    Date = item.Date,
                    DateLabel = item.DateLabel,
                    RevenueAmount = item.RevenueAmount,
                    CostAmount = item.CostAmount,
                    NetProfitAmount = item.NetProfitAmount
                }).ToList(),
                Last30Days = source.ProfitTrend.Last30Days.Select(item => new FinanceOverviewProfitTrendPointResponse
                {
                    Date = item.Date,
                    DateLabel = item.DateLabel,
                    RevenueAmount = item.RevenueAmount,
                    CostAmount = item.CostAmount,
                    NetProfitAmount = item.NetProfitAmount
                }).ToList()
            },
            IncomeComposition = new FinanceOverviewIncomeCompositionResponse
            {
                TotalAmount = source.IncomeComposition.TotalAmount,
                Items = source.IncomeComposition.Items.Select(item => new FinanceOverviewIncomeCompositionItemResponse
                {
                    Channel = item.Channel,
                    ChannelText = item.ChannelText,
                    Amount = item.Amount,
                    Percentage = item.Percentage
                }).ToList()
            },
            CostComposition = new FinanceOverviewCostCompositionResponse
            {
                TotalAmount = source.CostComposition.TotalAmount,
                Items = source.CostComposition.Items.Select(item => new FinanceOverviewCostCompositionItemResponse
                {
                    Category = item.Category,
                    CategoryText = item.CategoryText,
                    Amount = item.Amount,
                    Percentage = item.Percentage
                }).ToList()
            },
            TopProducts = new FinanceOverviewTopProductResponse
            {
                PeriodDays = source.TopProducts.PeriodDays,
                Items = source.TopProducts.Items.Select(item => new FinanceOverviewTopProductItemResponse
                {
                    Rank = item.Rank,
                    ProductName = item.ProductName,
                    SalesQuantity = item.SalesQuantity,
                    RevenueAmount = item.RevenueAmount,
                    Percentage = item.Percentage
                }).ToList()
            }
        };
    }

    private static FinanceOverviewKpiCardResponse MapKpi(FinanceOverviewKpiCardDto source)
    {
        return new FinanceOverviewKpiCardResponse
        {
            Amount = source.Amount,
            CompareAmount = source.CompareAmount,
            ChangeRate = source.ChangeRate,
            Trend = source.Trend,
            CompareLabel = source.CompareLabel
        };
    }
}
