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/list")]
public sealed class CustomerController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService)
    : BaseApiController
{
    private const string ViewPermission = "tenant:customer:list:view";
    private const string ManagePermission = "tenant:customer:list:manage";
    private const string ProfilePermission = "tenant:customer:profile:view";

    /// <summary>
    /// 获取客户列表。
    /// </summary>
    [HttpGet("list")]
    [PermissionAuthorize(ViewPermission, ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerListResultResponse>> List(
        [FromQuery] CustomerListRequest request,
        CancellationToken cancellationToken)
    {
        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);

        var result = await mediator.Send(new SearchCustomerListQuery
        {
            VisibleStoreIds = visibleStoreIds,
            Keyword = request.Keyword,
            Tag = request.Tag,
            OrderCountRange = request.OrderCountRange,
            RegisterPeriodDays = ParseRegisterPeriodDays(request.RegisterPeriod),
            Page = Math.Max(1, request.Page),
            PageSize = Math.Clamp(request.PageSize, 1, 200)
        }, cancellationToken);

        return ApiResponse<CustomerListResultResponse>.Ok(new CustomerListResultResponse
        {
            Items = result.Items.Select(MapListItem).ToList(),
            Total = result.TotalCount,
            Page = result.Page,
            PageSize = result.PageSize
        });
    }

    /// <summary>
    /// 获取客户列表统计。
    /// </summary>
    [HttpGet("stats")]
    [PermissionAuthorize(ViewPermission, ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerListStatsResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerListStatsResponse>> Stats(
        [FromQuery] CustomerListFilterRequest request,
        CancellationToken cancellationToken)
    {
        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);

        var result = await mediator.Send(new GetCustomerListStatsQuery
        {
            VisibleStoreIds = visibleStoreIds,
            Keyword = request.Keyword,
            Tag = request.Tag,
            OrderCountRange = request.OrderCountRange,
            RegisterPeriodDays = ParseRegisterPeriodDays(request.RegisterPeriod)
        }, cancellationToken);

        return ApiResponse<CustomerListStatsResponse>.Ok(new CustomerListStatsResponse
        {
            TotalCustomers = result.TotalCustomers,
            MonthlyNewCustomers = result.MonthlyNewCustomers,
            MonthlyGrowthRatePercent = result.MonthlyGrowthRatePercent,
            ActiveCustomers = result.ActiveCustomers,
            AverageAmountLast30Days = result.AverageAmountLast30Days
        });
    }

    /// <summary>
    /// 获取客户详情（一级抽屉）。
    /// </summary>
    [HttpGet("detail")]
    [PermissionAuthorize(ViewPermission, ManagePermission)]
    [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(ProfilePermission)]
    [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>
    /// 导出客户 CSV。
    /// </summary>
    [HttpGet("export")]
    [PermissionAuthorize(ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<CustomerExportResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<CustomerExportResponse>> Export(
        [FromQuery] CustomerListFilterRequest request,
        CancellationToken cancellationToken)
    {
        var visibleStoreIds = await ResolveVisibleStoreIdsAsync(request.StoreId, cancellationToken);

        var result = await mediator.Send(new ExportCustomerCsvQuery
        {
            VisibleStoreIds = visibleStoreIds,
            Keyword = request.Keyword,
            Tag = request.Tag,
            OrderCountRange = request.OrderCountRange,
            RegisterPeriodDays = ParseRegisterPeriodDays(request.RegisterPeriod)
        }, 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 int? ParseRegisterPeriodDays(string? registerPeriod)
    {
        var normalized = (registerPeriod ?? string.Empty).Trim().ToLowerInvariant();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            return null;
        }

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

    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 CustomerListItemResponse MapListItem(CustomerListItemDto source)
    {
        return new CustomerListItemResponse
        {
            CustomerKey = source.CustomerKey,
            Name = source.Name,
            PhoneMasked = source.PhoneMasked,
            AvatarText = source.AvatarText,
            AvatarColor = source.AvatarColor,
            OrderCount = source.OrderCount,
            OrderCountBarPercent = source.OrderCountBarPercent,
            TotalAmount = source.TotalAmount,
            AverageAmount = source.AverageAmount,
            LastOrderAt = ToDateOnly(source.LastOrderAt),
            Tags = source.Tags.Select(MapTag).ToList(),
            IsDimmed = source.IsDimmed
        };
    }

    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 CustomerTagResponse MapTag(CustomerTagDto source)
    {
        return new CustomerTagResponse
        {
            Code = source.Code,
            Label = source.Label,
            Tone = source.Tone
        };
    }

    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 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 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);
    }
}
