using System.Globalization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TakeoutSaaS.Application.App.Coupons.PunchCard.Commands;
using TakeoutSaaS.Application.App.Coupons.PunchCard.Dto;
using TakeoutSaaS.Application.App.Coupons.PunchCard.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.Results;
using TakeoutSaaS.Shared.Web.Api;
using TakeoutSaaS.TenantApi.Contracts.Marketing;

namespace TakeoutSaaS.TenantApi.Controllers;

/// <summary>
/// 营销中心次卡管理。
/// </summary>
[ApiVersion("1.0")]
[Authorize]
[Route("api/tenant/v{version:apiVersion}/marketing/punch-card")]
public sealed class MarketingPunchCardController(
    IMediator mediator,
    TakeoutAppDbContext dbContext,
    StoreContextService storeContextService)
    : BaseApiController
{
    private const string ViewPermission = "tenant:marketing:punch-card:view";
    private const string ManagePermission = "tenant:marketing:punch-card:manage";

    /// <summary>
    /// 获取次卡列表。
    /// </summary>
    [HttpGet("list")]
    [PermissionAuthorize(ViewPermission, ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<PunchCardListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<PunchCardListResultResponse>> List(
        [FromQuery] PunchCardListRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var result = await mediator.Send(new GetPunchCardTemplateListQuery
        {
            StoreId = storeId,
            Keyword = request.Keyword,
            Status = request.Status,
            Page = request.Page,
            PageSize = request.PageSize
        }, cancellationToken);

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

    /// <summary>
    /// 获取次卡详情。
    /// </summary>
    [HttpGet("detail")]
    [PermissionAuthorize(ViewPermission, ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<PunchCardDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<PunchCardDetailResponse>> Detail(
        [FromQuery] PunchCardDetailRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var result = await mediator.Send(new GetPunchCardTemplateDetailQuery
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseRequiredSnowflake(request.PunchCardId, nameof(request.PunchCardId))
        }, cancellationToken);

        if (result is null)
        {
            return ApiResponse<PunchCardDetailResponse>.Error(ErrorCodes.NotFound, "次卡不存在");
        }

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

    /// <summary>
    /// 保存次卡。
    /// </summary>
    [HttpPost("save")]
    [PermissionAuthorize(ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<PunchCardDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<PunchCardDetailResponse>> Save(
        [FromBody] SavePunchCardRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var result = await mediator.Send(new SavePunchCardTemplateCommand
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseSnowflakeOrNull(request.Id),
            Name = request.Name,
            CoverImageUrl = request.CoverImageUrl,
            SalePrice = request.SalePrice,
            OriginalPrice = request.OriginalPrice,
            TotalTimes = request.TotalTimes,
            ValidityType = request.ValidityType,
            ValidityDays = request.ValidityDays,
            ValidFrom = ParseDateOrNull(request.ValidFrom, nameof(request.ValidFrom)),
            ValidTo = ParseDateOrNull(request.ValidTo, nameof(request.ValidTo)),
            ScopeType = request.ScopeType,
            ScopeCategoryIds = StoreApiHelpers.ParseSnowflakeList(request.ScopeCategoryIds),
            ScopeTagIds = StoreApiHelpers.ParseSnowflakeList(request.ScopeTagIds),
            ScopeProductIds = StoreApiHelpers.ParseSnowflakeList(request.ScopeProductIds),
            UsageMode = request.UsageMode,
            UsageCapAmount = request.UsageCapAmount,
            DailyLimit = request.DailyLimit,
            PerOrderLimit = request.PerOrderLimit,
            PerUserPurchaseLimit = request.PerUserPurchaseLimit,
            AllowTransfer = request.AllowTransfer,
            ExpireStrategy = request.ExpireStrategy,
            Description = request.Description,
            NotifyChannels = request.NotifyChannels
        }, cancellationToken);

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

    /// <summary>
    /// 修改次卡状态。
    /// </summary>
    [HttpPost("status")]
    [PermissionAuthorize(ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<PunchCardDetailResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<PunchCardDetailResponse>> ChangeStatus(
        [FromBody] ChangePunchCardStatusRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var result = await mediator.Send(new ChangePunchCardTemplateStatusCommand
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseRequiredSnowflake(request.PunchCardId, nameof(request.PunchCardId)),
            Status = request.Status
        }, cancellationToken);

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

    /// <summary>
    /// 删除次卡。
    /// </summary>
    [HttpPost("delete")]
    [PermissionAuthorize(ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<object>> Delete(
        [FromBody] DeletePunchCardRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        await mediator.Send(new DeletePunchCardTemplateCommand
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseRequiredSnowflake(request.PunchCardId, nameof(request.PunchCardId))
        }, cancellationToken);

        return ApiResponse<object>.Ok(null);
    }

    /// <summary>
    /// 获取次卡使用记录。
    /// </summary>
    [HttpGet("usage-record/list")]
    [PermissionAuthorize(ViewPermission, ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<PunchCardUsageRecordListResultResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<PunchCardUsageRecordListResultResponse>> UsageRecordList(
        [FromQuery] PunchCardUsageRecordListRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var result = await mediator.Send(new GetPunchCardUsageRecordListQuery
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseSnowflakeOrNull(request.PunchCardId),
            Status = request.Status,
            Keyword = request.Keyword,
            Page = request.Page,
            PageSize = request.PageSize
        }, cancellationToken);

        return ApiResponse<PunchCardUsageRecordListResultResponse>.Ok(new PunchCardUsageRecordListResultResponse
        {
            Items = result.Items.Select(MapUsageRecord).ToList(),
            Page = result.Page,
            PageSize = result.PageSize,
            TotalCount = result.TotalCount,
            Stats = new PunchCardUsageStatsResponse
            {
                TodayUsedCount = result.Stats.TodayUsedCount,
                MonthUsedCount = result.Stats.MonthUsedCount,
                ExpiringSoonCount = result.Stats.ExpiringSoonCount
            },
            TemplateOptions = result.TemplateOptions.Select(item => new PunchCardTemplateOptionResponse
            {
                TemplateId = item.TemplateId.ToString(),
                Name = item.Name
            }).ToList()
        });
    }

    /// <summary>
    /// 导出次卡使用记录。
    /// </summary>
    [HttpGet("usage-record/export")]
    [PermissionAuthorize(ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<PunchCardUsageRecordExportResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<PunchCardUsageRecordExportResponse>> ExportUsageRecord(
        [FromQuery] ExportPunchCardUsageRecordRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var result = await mediator.Send(new ExportPunchCardUsageRecordCsvQuery
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseSnowflakeOrNull(request.PunchCardId),
            Status = request.Status,
            Keyword = request.Keyword
        }, cancellationToken);

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

    /// <summary>
    /// 写入次卡使用记录。
    /// </summary>
    [HttpPost("usage-record/write")]
    [PermissionAuthorize(ManagePermission)]
    [ProducesResponseType(typeof(ApiResponse<PunchCardUsageRecordResponse>), StatusCodes.Status200OK)]
    public async Task<ApiResponse<PunchCardUsageRecordResponse>> WriteUsageRecord(
        [FromBody] WritePunchCardUsageRecordRequest request,
        CancellationToken cancellationToken)
    {
        var storeId = StoreApiHelpers.ParseRequiredSnowflake(request.StoreId, nameof(request.StoreId));
        await EnsureStoreAccessibleAsync(storeId, cancellationToken);

        var result = await mediator.Send(new WritePunchCardUsageRecordCommand
        {
            StoreId = storeId,
            TemplateId = StoreApiHelpers.ParseRequiredSnowflake(request.PunchCardId, nameof(request.PunchCardId)),
            InstanceId = StoreApiHelpers.ParseSnowflakeOrNull(request.PunchCardInstanceId),
            InstanceNo = request.PunchCardInstanceNo,
            MemberName = request.MemberName,
            MemberPhoneMasked = request.MemberPhoneMasked,
            ProductName = request.ProductName,
            UsedAt = request.UsedAt,
            UsedTimes = request.UsedTimes,
            ExtraPayAmount = request.ExtraPayAmount
        }, cancellationToken);

        return ApiResponse<PunchCardUsageRecordResponse>.Ok(MapUsageRecord(result));
    }

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

    private static DateTime? ParseDateOrNull(string? value, string fieldName)
    {
        return string.IsNullOrWhiteSpace(value)
            ? null
            : StoreApiHelpers.ParseDateOnly(value, fieldName);
    }

    private static PunchCardListItemResponse MapListItem(PunchCardListItemDto source)
    {
        return new PunchCardListItemResponse
        {
            Id = source.Id.ToString(),
            Name = source.Name,
            CoverImageUrl = source.CoverImageUrl,
            SalePrice = source.SalePrice,
            OriginalPrice = source.OriginalPrice,
            TotalTimes = source.TotalTimes,
            ValiditySummary = source.ValiditySummary,
            ScopeType = source.ScopeType,
            UsageMode = source.UsageMode,
            UsageCapAmount = source.UsageCapAmount,
            DailyLimit = source.DailyLimit,
            Status = source.Status,
            IsDimmed = source.IsDimmed,
            SoldCount = source.SoldCount,
            ActiveCount = source.ActiveCount,
            RevenueAmount = source.RevenueAmount,
            UpdatedAt = ToDateTime(source.UpdatedAt)
        };
    }

    private static PunchCardStatsResponse MapTemplateStats(PunchCardStatsDto source)
    {
        return new PunchCardStatsResponse
        {
            OnSaleCount = source.OnSaleCount,
            TotalSoldCount = source.TotalSoldCount,
            TotalRevenueAmount = source.TotalRevenueAmount,
            ActiveInUseCount = source.ActiveInUseCount
        };
    }

    private static PunchCardDetailResponse MapDetail(PunchCardDetailDto source)
    {
        return new PunchCardDetailResponse
        {
            Id = source.Id.ToString(),
            StoreId = source.StoreId.ToString(),
            Name = source.Name,
            CoverImageUrl = source.CoverImageUrl,
            SalePrice = source.SalePrice,
            OriginalPrice = source.OriginalPrice,
            TotalTimes = source.TotalTimes,
            ValidityType = source.ValidityType,
            ValidityDays = source.ValidityDays,
            ValidFrom = ToDateOnly(source.ValidFrom),
            ValidTo = ToDateOnly(source.ValidTo),
            Scope = new PunchCardScopeResponse
            {
                ScopeType = source.Scope.ScopeType,
                CategoryIds = source.Scope.CategoryIds.Select(item => item.ToString()).ToList(),
                TagIds = source.Scope.TagIds.Select(item => item.ToString()).ToList(),
                ProductIds = source.Scope.ProductIds.Select(item => item.ToString()).ToList()
            },
            UsageMode = source.UsageMode,
            UsageCapAmount = source.UsageCapAmount,
            DailyLimit = source.DailyLimit,
            PerOrderLimit = source.PerOrderLimit,
            PerUserPurchaseLimit = source.PerUserPurchaseLimit,
            AllowTransfer = source.AllowTransfer,
            ExpireStrategy = source.ExpireStrategy,
            Description = source.Description,
            NotifyChannels = source.NotifyChannels.ToList(),
            Status = source.Status,
            SoldCount = source.SoldCount,
            ActiveCount = source.ActiveCount,
            RevenueAmount = source.RevenueAmount,
            UpdatedAt = ToDateTime(source.UpdatedAt)
        };
    }

    private static PunchCardUsageRecordResponse MapUsageRecord(PunchCardUsageRecordDto source)
    {
        return new PunchCardUsageRecordResponse
        {
            Id = source.Id.ToString(),
            RecordNo = source.RecordNo,
            PunchCardId = source.PunchCardTemplateId.ToString(),
            PunchCardName = source.PunchCardName,
            PunchCardInstanceId = source.PunchCardInstanceId.ToString(),
            MemberName = source.MemberName,
            MemberPhoneMasked = source.MemberPhoneMasked,
            ProductName = source.ProductName,
            UsedAt = ToDateTime(source.UsedAt),
            UsedTimes = source.UsedTimes,
            RemainingTimesAfterUse = source.RemainingTimesAfterUse,
            TotalTimes = source.TotalTimes,
            DisplayStatus = source.DisplayStatus,
            ExtraPayAmount = source.ExtraPayAmount
        };
    }

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

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