using System.Globalization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TakeoutSaaS.Application.App.Finance.Cost.Commands;
using TakeoutSaaS.Application.App.Finance.Cost.Dto;
using TakeoutSaaS.Application.App.Finance.Cost.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/cost")]
public sealed class FinanceCostController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService)
    : BaseApiController
{
    private const string ViewPermission = "tenant:finance:cost:view";
    private const string ManagePermission = "tenant:finance:cost:manage";

    /// <summary>
    /// 查询成本录入数据。
    /// </summary>
    [HttpGet("entry")]
    [PermissionAuthorize(ViewPermission, ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceCostEntryResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceCostEntryResponse>> Entry(
        [FromQuery] FinanceCostEntryRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析维度与作用域。
        var scope = await ParseScopeAsync(request, cancellationToken);

        // 2. 查询录入数据并映射响应。
        var result = await mediator.Send(new GetFinanceCostEntryQuery
        {
            Dimension = scope.Dimension,
            StoreId = scope.StoreId,
            CostMonth = scope.CostMonth
        }, cancellationToken);

        return ApiResponse<FinanceCostEntryResponse>.Ok(MapEntry(result));
    }

    /// <summary>
    /// 保存成本录入数据。
    /// </summary>
    [HttpPost("entry/save")]
    [PermissionAuthorize(ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceCostEntryResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceCostEntryResponse>> SaveEntry(
        [FromBody] SaveFinanceCostEntryRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析维度与作用域。
        var scope = await ParseScopeAsync(request, cancellationToken);

        // 2. 发起保存命令并映射响应。
        var result = await mediator.Send(new SaveFinanceCostEntryCommand
        {
            Dimension = scope.Dimension,
            StoreId = scope.StoreId,
            CostMonth = scope.CostMonth,
            Categories = (request.Categories ?? [])
                .Select(MapSaveCategory)
                .ToList()
        }, cancellationToken);

        return ApiResponse<FinanceCostEntryResponse>.Ok(MapEntry(result));
    }

    /// <summary>
    /// 查询成本分析数据。
    /// </summary>
    [HttpGet("analysis")]
    [PermissionAuthorize(ViewPermission, ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<FinanceCostAnalysisResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<FinanceCostAnalysisResponse>> Analysis(
        [FromQuery] FinanceCostAnalysisRequest request,
        CancellationToken cancellationToken)
    {
        // 1. 解析维度与作用域。
        var scope = await ParseScopeAsync(request, cancellationToken);

        // 2. 查询分析数据并映射响应。
        var result = await mediator.Send(new GetFinanceCostAnalysisQuery
        {
            Dimension = scope.Dimension,
            StoreId = scope.StoreId,
            CostMonth = scope.CostMonth,
            TrendMonthCount = Math.Clamp(request.TrendMonthCount, 3, 12)
        }, cancellationToken);

        return ApiResponse<FinanceCostAnalysisResponse>.Ok(MapAnalysis(result));
    }

    private async Task<(FinanceCostDimension Dimension, long? StoreId, DateTime CostMonth)> ParseScopeAsync(
        FinanceCostScopeRequest request,
        CancellationToken cancellationToken)
    {
        var dimension = ParseDimension(request.Dimension);
        var costMonth = ParseMonthOrDefault(request.Month);

        if (dimension == FinanceCostDimension.Tenant)
        {
            return (dimension, null, costMonth);
        }

        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);
        return (dimension, storeId, costMonth);
    }

    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)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "" or "tenant" => FinanceCostDimension.Tenant,
            "store" => FinanceCostDimension.Store,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "dimension 非法")
        };
    }

    private static DateTime ParseMonthOrDefault(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            var utcNow = DateTime.UtcNow;
            return new DateTime(utcNow.Year, utcNow.Month, 1, 0, 0, 0, DateTimeKind.Utc);
        }

        if (DateTime.TryParseExact(
                value.Trim(),
                "yyyy-MM",
                CultureInfo.InvariantCulture,
                DateTimeStyles.None,
                out var parsed))
        {
            return new DateTime(parsed.Year, parsed.Month, 1, 0, 0, 0, DateTimeKind.Utc);
        }

        throw new BusinessException(ErrorCodes.BadRequest, "month 格式必须为 yyyy-MM");
    }

    private static FinanceCostCategory ParseCategory(string? value)
    {
        var normalized = (value ?? string.Empty).Trim().ToLowerInvariant();
        return normalized switch
        {
            "food" => FinanceCostCategory.FoodMaterial,
            "labor" => FinanceCostCategory.Labor,
            "fixed" => FinanceCostCategory.FixedExpense,
            "packaging" => FinanceCostCategory.PackagingConsumable,
            _ => throw new BusinessException(ErrorCodes.BadRequest, "category 非法")
        };
    }

    private static SaveFinanceCostCategoryCommandItem MapSaveCategory(SaveFinanceCostCategoryRequest source)
    {
        return new SaveFinanceCostCategoryCommandItem
        {
            Category = ParseCategory(source.Category),
            TotalAmount = source.TotalAmount,
            Items = (source.Items ?? [])
                .Select(item => new SaveFinanceCostDetailCommandItem
                {
                    ItemId = StoreApiHelpers.ParseSnowflakeOrNull(item.ItemId),
                    ItemName = item.ItemName,
                    Amount = item.Amount,
                    Quantity = item.Quantity,
                    UnitPrice = item.UnitPrice,
                    SortOrder = item.SortOrder
                })
                .ToList()
        };
    }

    private static FinanceCostEntryResponse MapEntry(FinanceCostEntryDto source)
    {
        return new FinanceCostEntryResponse
        {
            Dimension = source.Dimension,
            StoreId = source.StoreId,
            Month = source.Month,
            MonthRevenue = source.MonthRevenue,
            TotalCost = source.TotalCost,
            CostRate = source.CostRate,
            Categories = source.Categories.Select(category => new FinanceCostEntryCategoryResponse
            {
                Category = category.Category,
                CategoryText = category.CategoryText,
                TotalAmount = category.TotalAmount,
                Percentage = category.Percentage,
                Items = category.Items.Select(item => new FinanceCostEntryDetailResponse
                {
                    ItemId = item.ItemId,
                    ItemName = item.ItemName,
                    Amount = item.Amount,
                    Quantity = item.Quantity,
                    UnitPrice = item.UnitPrice,
                    SortOrder = item.SortOrder
                }).ToList()
            }).ToList()
        };
    }

    private static FinanceCostAnalysisResponse MapAnalysis(FinanceCostAnalysisDto source)
    {
        return new FinanceCostAnalysisResponse
        {
            Dimension = source.Dimension,
            StoreId = source.StoreId,
            Month = source.Month,
            Stats = new FinanceCostAnalysisStatsResponse
            {
                TotalCost = source.Stats.TotalCost,
                FoodCostRate = source.Stats.FoodCostRate,
                AverageCostPerPaidOrder = source.Stats.AverageCostPerPaidOrder,
                MonthOnMonthChangeRate = source.Stats.MonthOnMonthChangeRate,
                Revenue = source.Stats.Revenue,
                PaidOrderCount = source.Stats.PaidOrderCount
            },
            Trend = source.Trend.Select(item => new FinanceCostTrendPointResponse
            {
                Month = item.Month,
                TotalCost = item.TotalCost,
                Revenue = item.Revenue,
                CostRate = item.CostRate
            }).ToList(),
            Composition = source.Composition.Select(item => new FinanceCostCompositionResponse
            {
                Category = item.Category,
                CategoryText = item.CategoryText,
                Amount = item.Amount,
                Percentage = item.Percentage
            }).ToList(),
            DetailRows = source.DetailRows.Select(item => new FinanceCostMonthlyDetailResponse
            {
                Month = item.Month,
                FoodAmount = item.FoodAmount,
                LaborAmount = item.LaborAmount,
                FixedAmount = item.FixedAmount,
                PackagingAmount = item.PackagingAmount,
                TotalCost = item.TotalCost,
                CostRate = item.CostRate
            }).ToList()
        };
    }
}
