using System.Globalization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Application.App.Customers.Dto;
using TakeoutSaaS.Application.App.Customers.Queries;
using TakeoutSaaS.Application.App.Stores.Services;
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.Customer;

namespace TakeoutSaaS.TenantApi.Controllers;

/// <summary>
/// 客户分析。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/tenant/v{version:apiVersion}/customer/analysis")]
public sealed class CustomerAnalysisController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService)
    : BaseApiController
{
    private const string ViewPermission = "tenant:customer:analysis:view";

    /// <summary>
    /// 获取客户分析总览。
    /// </summary>
    [HttpGet("overview")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerAnalysisOverviewResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerAnalysisOverviewResponse>> Overview(
        [FromQuery] CustomerAnalysisOverviewRequest request,
        CancellationToken cancellationToken)
    {
        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);
        var (periodCode, periodDays) = ParsePeriod(request.Period);

        var result = await mediator.Send(new GetCustomerAnalysisOverviewQuery
        {
            VisibleStoreIds = visibleStoreIds,
            PeriodCode = periodCode,
            PeriodDays = periodDays
        }, cancellationToken);

        return ApiResponse<CustomerAnalysisOverviewResponse>.Ok(MapOverview(result));
    }

    /// <summary>
    /// 获取客群明细。
    /// </summary>
    [HttpGet("segment/list")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerAnalysisSegmentListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerAnalysisSegmentListResultResponse>> SegmentList(
        [FromQuery] CustomerAnalysisSegmentListRequest request,
        CancellationToken cancellationToken)
    {
        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);
        var (periodCode, periodDays) = ParsePeriod(request.Period);

        var result = await mediator.Send(new GetCustomerAnalysisSegmentListQuery
        {
            VisibleStoreIds = visibleStoreIds,
            PeriodCode = periodCode,
            PeriodDays = periodDays,
            SegmentCode = request.SegmentCode,
            Keyword = request.Keyword,
            Page = Math.Max(1, request.Page),
            PageSize = Math.Clamp(request.PageSize, 1, 200)
        }, cancellationToken);

        return ApiResponse<CustomerAnalysisSegmentListResultResponse>.Ok(MapSegmentList(result));
    }

    /// <summary>
    /// 获取客户详情（分析页二级抽屉）。
    /// </summary>
    [HttpGet("detail")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerDetailResponse>> Detail(
        [FromQuery] CustomerDetailRequest request,
        CancellationToken cancellationToken)
    {
        var customerKey = NormalizePhone(request.CustomerKey);
        if (string.IsNullOrWhiteSpace(customerKey))
        {
            return ApiResponse<CustomerDetailResponse>.Error(ErrorCodes.BadRequest, "customerKey 非法");
        }

        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);
        var result = await mediator.Send(new GetCustomerDetailQuery
        {
            VisibleStoreIds = visibleStoreIds,
            CustomerKey = customerKey
        }, cancellationToken);

        if (result is null)
        {
            return ApiResponse<CustomerDetailResponse>.Error(ErrorCodes.NotFound, "客户不存在");
        }

        return ApiResponse<CustomerDetailResponse>.Ok(MapDetail(result));
    }

    /// <summary>
    /// 获取客户完整画像（分析页二级抽屉）。
    /// </summary>
    [HttpGet("profile")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerProfileResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerProfileResponse>> Profile(
        [FromQuery] CustomerProfileRequest request,
        CancellationToken cancellationToken)
    {
        var customerKey = NormalizePhone(request.CustomerKey);
        if (string.IsNullOrWhiteSpace(customerKey))
        {
            return ApiResponse<CustomerProfileResponse>.Error(ErrorCodes.BadRequest, "customerKey 非法");
        }

        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);
        var result = await mediator.Send(new GetCustomerProfileQuery
        {
            VisibleStoreIds = visibleStoreIds,
            CustomerKey = customerKey
        }, cancellationToken);

        if (result is null)
        {
            return ApiResponse<CustomerProfileResponse>.Error(ErrorCodes.NotFound, "客户不存在");
        }

        return ApiResponse<CustomerProfileResponse>.Ok(MapProfile(result));
    }

    /// <summary>
    /// 获取会员详情。
    /// </summary>
    [HttpGet("member/detail")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerMemberDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerMemberDetailResponse>> MemberDetail(
        [FromQuery] CustomerMemberDetailRequest request,
        CancellationToken cancellationToken)
    {
        var customerKey = NormalizePhone(request.CustomerKey);
        if (string.IsNullOrWhiteSpace(customerKey))
        {
            return ApiResponse<CustomerMemberDetailResponse>.Error(ErrorCodes.BadRequest, "customerKey 非法");
        }

        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);

        var result = await mediator.Send(new GetCustomerMemberDetailQuery
        {
            VisibleStoreIds = visibleStoreIds,
            CustomerKey = customerKey
        }, cancellationToken);

        if (result is null)
        {
            return ApiResponse<CustomerMemberDetailResponse>.Error(ErrorCodes.NotFound, "客户不存在");
        }

        return ApiResponse<CustomerMemberDetailResponse>.Ok(MapMemberDetail(result));
    }

    /// <summary>
    /// 导出客户分析报表。
    /// </summary>
    [HttpGet("export")]
    [PermissionAuthorize(ViewPermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerExportResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerExportResponse>> Export(
        [FromQuery] CustomerAnalysisExportRequest request,
        CancellationToken cancellationToken)
    {
        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);
        var (periodCode, periodDays) = ParsePeriod(request.Period);

        var result = await mediator.Send(new ExportCustomerAnalysisCsvQuery
        {
            VisibleStoreIds = visibleStoreIds,
            PeriodCode = periodCode,
            PeriodDays = periodDays
        }, cancellationToken);

        return ApiResponse<CustomerExportResponse>.Ok(new CustomerExportResponse
        {
            FileName = result.FileName,
            FileContentBase64 = result.FileContentBase64,
            TotalCount = result.TotalCount
        });
    }

    private async Task<IReadOnlyCollection<long>> ResolveVisibleStoreIdsAsync(
        string? storeId,
        CancellationToken cancellationToken)
    {
        var (tenantId, merchantId) = StoreApiHelpers.GetTenantMerchantContext(storeContextService);

        if (!string.IsNullOrWhiteSpace(storeId))
        {
            var parsedStoreId = StoreApiHelpers.ParseRequiredSnowflake(storeId, nameof(storeId));
            await StoreApiHelpers.EnsureStoreAccessibleAsync(
                dbContext,
                tenantId,
                merchantId,
                parsedStoreId,
                cancellationToken);
            return [parsedStoreId];
        }

        var allStoreIds = await dbContext.Stores
            .AsNoTracking()
            .Where(item => item.TenantId == tenantId && item.MerchantId == merchantId)
            .Select(item => item.Id)
            .OrderBy(item => item)
            .ToListAsync(cancellationToken);

        if (allStoreIds.Count == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "当前商户下不存在可用门店");
        }

        return allStoreIds;
    }

    private static (string PeriodCode, int PeriodDays) ParsePeriod(string? period)
    {
        var normalized = (period ?? string.Empty).Trim().ToLowerInvariant();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            return ("30d", 30);
        }

        return normalized switch
        {
            "7" or "7d" => ("7d", 7),
            "30" or "30d" => ("30d", 30),
            "90" or "90d" => ("90d", 90),
            "365" or "365d" or "1y" or "1year" => ("365d", 365),
            _ => throw new BusinessException(ErrorCodes.BadRequest, "period 参数不合法")
        };
    }

    private static string NormalizePhone(string? value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return string.Empty;
        }

        var chars = value.Where(char.IsDigit).ToArray();
        return chars.Length == 0 ? string.Empty : new string(chars);
    }

    private static CustomerAnalysisOverviewResponse MapOverview(CustomerAnalysisOverviewDto source)
    {
        return new CustomerAnalysisOverviewResponse
        {
            PeriodCode = source.PeriodCode,
            PeriodDays = source.PeriodDays,
            TotalCustomers = source.TotalCustomers,
            NewCustomers = source.NewCustomers,
            GrowthRatePercent = source.GrowthRatePercent,
            NewCustomersDailyAverage = source.NewCustomersDailyAverage,
            ActiveCustomers = source.ActiveCustomers,
            ActiveRatePercent = source.ActiveRatePercent,
            AverageLifetimeValue = source.AverageLifetimeValue,
            GrowthTrend = source.GrowthTrend.Select(MapTrendPoint).ToList(),
            Composition = source.Composition.Select(MapCompositionItem).ToList(),
            AmountDistribution = source.AmountDistribution.Select(MapAmountDistributionItem).ToList(),
            RfmRows = source.RfmRows.Select(MapRfmRow).ToList(),
            TopCustomers = source.TopCustomers.Select(MapTopCustomer).ToList()
        };
    }

    private static CustomerAnalysisTrendPointResponse MapTrendPoint(CustomerAnalysisTrendPointDto source)
    {
        return new CustomerAnalysisTrendPointResponse
        {
            Label = source.Label,
            Value = source.Value
        };
    }

    private static CustomerAnalysisCompositionItemResponse MapCompositionItem(CustomerAnalysisCompositionItemDto source)
    {
        return new CustomerAnalysisCompositionItemResponse
        {
            SegmentCode = source.SegmentCode,
            Label = source.Label,
            Count = source.Count,
            Percent = source.Percent,
            Tone = source.Tone
        };
    }

    private static CustomerAnalysisAmountDistributionItemResponse MapAmountDistributionItem(
        CustomerAnalysisAmountDistributionItemDto source)
    {
        return new CustomerAnalysisAmountDistributionItemResponse
        {
            SegmentCode = source.SegmentCode,
            Label = source.Label,
            Count = source.Count,
            Percent = source.Percent
        };
    }

    private static CustomerAnalysisRfmRowResponse MapRfmRow(CustomerAnalysisRfmRowDto source)
    {
        return new CustomerAnalysisRfmRowResponse
        {
            Label = source.Label,
            Cells = source.Cells.Select(MapRfmCell).ToList()
        };
    }

    private static CustomerAnalysisRfmCellResponse MapRfmCell(CustomerAnalysisRfmCellDto source)
    {
        return new CustomerAnalysisRfmCellResponse
        {
            SegmentCode = source.SegmentCode,
            Label = source.Label,
            Count = source.Count,
            Tone = source.Tone
        };
    }

    private static CustomerAnalysisTopCustomerResponse MapTopCustomer(CustomerAnalysisTopCustomerDto source)
    {
        return new CustomerAnalysisTopCustomerResponse
        {
            Rank = source.Rank,
            CustomerKey = source.CustomerKey,
            Name = source.Name,
            PhoneMasked = source.PhoneMasked,
            TotalAmount = source.TotalAmount,
            OrderCount = source.OrderCount,
            AverageAmount = source.AverageAmount,
            LastOrderAt = ToDateOnly(source.LastOrderAt),
            Tags = source.Tags.Select(MapTag).ToList()
        };
    }

    private static CustomerDetailResponse MapDetail(CustomerDetailDto source)
    {
        return new CustomerDetailResponse
        {
            CustomerKey = source.CustomerKey,
            Name = source.Name,
            PhoneMasked = source.PhoneMasked,
            RegisteredAt = ToDateOnly(source.RegisteredAt),
            FirstOrderAt = ToDateOnly(source.FirstOrderAt),
            Source = source.Source,
            Tags = source.Tags.Select(MapTag).ToList(),
            Member = MapMember(source.Member),
            TotalOrders = source.TotalOrders,
            TotalAmount = source.TotalAmount,
            AverageAmount = source.AverageAmount,
            RepurchaseRatePercent = source.RepurchaseRatePercent,
            Preference = MapPreference(source.Preference),
            TopProducts = source.TopProducts.Select(MapTopProduct).ToList(),
            Trend = source.Trend.Select(MapTrend).ToList(),
            RecentOrders = source.RecentOrders.Select(MapRecentOrder).ToList()
        };
    }

    private static CustomerProfileResponse MapProfile(CustomerProfileDto source)
    {
        return new CustomerProfileResponse
        {
            CustomerKey = source.CustomerKey,
            Name = source.Name,
            PhoneMasked = source.PhoneMasked,
            RegisteredAt = ToDateOnly(source.RegisteredAt),
            FirstOrderAt = ToDateOnly(source.FirstOrderAt),
            Source = source.Source,
            Tags = source.Tags.Select(MapTag).ToList(),
            Member = MapMember(source.Member),
            TotalOrders = source.TotalOrders,
            TotalAmount = source.TotalAmount,
            AverageAmount = source.AverageAmount,
            RepurchaseRatePercent = source.RepurchaseRatePercent,
            AverageOrderIntervalDays = source.AverageOrderIntervalDays,
            Preference = MapPreference(source.Preference),
            TopProducts = source.TopProducts.Select(MapTopProduct).ToList(),
            Trend = source.Trend.Select(MapTrend).ToList(),
            RecentOrders = source.RecentOrders.Select(MapRecentOrder).ToList()
        };
    }

    private static CustomerAnalysisSegmentListResultResponse MapSegmentList(CustomerAnalysisSegmentListResultDto source)
    {
        return new CustomerAnalysisSegmentListResultResponse
        {
            SegmentCode = source.SegmentCode,
            SegmentTitle = source.SegmentTitle,
            SegmentDescription = source.SegmentDescription,
            Items = source.Items.Select(MapSegmentListItem).ToList(),
            Page = source.Page,
            PageSize = source.PageSize,
            TotalCount = source.TotalCount
        };
    }

    private static CustomerAnalysisSegmentListItemResponse MapSegmentListItem(CustomerAnalysisSegmentListItemDto source)
    {
        return new CustomerAnalysisSegmentListItemResponse
        {
            CustomerKey = source.CustomerKey,
            Name = source.Name,
            PhoneMasked = source.PhoneMasked,
            AvatarText = source.AvatarText,
            AvatarColor = source.AvatarColor,
            Tags = source.Tags.Select(MapTag).ToList(),
            IsMember = source.IsMember,
            MemberTierName = source.MemberTierName,
            TotalAmount = source.TotalAmount,
            OrderCount = source.OrderCount,
            AverageAmount = source.AverageAmount,
            RegisteredAt = ToDateOnly(source.RegisteredAt),
            LastOrderAt = ToDateOnly(source.LastOrderAt),
            IsDimmed = source.IsDimmed
        };
    }

    private static CustomerMemberDetailResponse MapMemberDetail(CustomerMemberDetailDto source)
    {
        return new CustomerMemberDetailResponse
        {
            CustomerKey = source.CustomerKey,
            Name = source.Name,
            PhoneMasked = source.PhoneMasked,
            Source = source.Source,
            RegisteredAt = ToDateOnly(source.RegisteredAt),
            LastOrderAt = ToDateOnly(source.LastOrderAt),
            Member = MapMember(source.Member),
            Tags = source.Tags.Select(MapTag).ToList(),
            TotalOrders = source.TotalOrders,
            TotalAmount = source.TotalAmount,
            AverageAmount = source.AverageAmount,
            RepurchaseRatePercent = source.RepurchaseRatePercent,
            RecentOrders = source.RecentOrders.Select(MapRecentOrder).ToList()
        };
    }

    private static CustomerTagResponse MapTag(CustomerTagDto source)
    {
        return new CustomerTagResponse
        {
            Code = source.Code,
            Label = source.Label,
            Tone = source.Tone
        };
    }

    private static CustomerMemberSummaryResponse MapMember(CustomerMemberSummaryDto source)
    {
        return new CustomerMemberSummaryResponse
        {
            IsMember = source.IsMember,
            TierName = source.TierName,
            PointsBalance = source.PointsBalance,
            GrowthValue = source.GrowthValue,
            JoinedAt = source.JoinedAt.HasValue ? ToDateOnly(source.JoinedAt.Value) : string.Empty
        };
    }

    private static CustomerPreferenceResponse MapPreference(CustomerPreferenceDto source)
    {
        return new CustomerPreferenceResponse
        {
            PreferredCategories = source.PreferredCategories.ToList(),
            PreferredOrderPeaks = source.PreferredOrderPeaks,
            PreferredDelivery = source.PreferredDelivery,
            PreferredPaymentMethod = source.PreferredPaymentMethod,
            AverageDeliveryDistance = source.AverageDeliveryDistance
        };
    }

    private static CustomerTopProductResponse MapTopProduct(CustomerTopProductDto source)
    {
        return new CustomerTopProductResponse
        {
            Rank = source.Rank,
            ProductName = source.ProductName,
            Count = source.Count,
            ProportionPercent = source.ProportionPercent
        };
    }

    private static CustomerTrendPointResponse MapTrend(CustomerTrendPointDto source)
    {
        return new CustomerTrendPointResponse
        {
            Label = source.Label,
            Amount = source.Amount
        };
    }

    private static CustomerRecentOrderResponse MapRecentOrder(CustomerRecentOrderDto source)
    {
        return new CustomerRecentOrderResponse
        {
            OrderNo = source.OrderNo,
            Amount = source.Amount,
            ItemsSummary = source.ItemsSummary,
            DeliveryType = source.DeliveryType,
            Status = source.Status,
            OrderedAt = ToDateTime(source.OrderedAt)
        };
    }

    private static string ToDateOnly(DateTime value)
    {
        return value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
    }

    private static string ToDateTime(DateTime value)
    {
        return value.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
    }
}
