package redpacket

import (
	"strconv"
	"strings"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/walletclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/response"
	walletv1 "hyapp.local/api/proto/wallet/v1"

	"github.com/gin-gonic/gin"
)

type Handler struct {
	wallet walletclient.Client
	audit  shared.OperationLogger
}

func New(wallet walletclient.Client, audit shared.OperationLogger) *Handler {
	return &Handler{wallet: wallet, audit: audit}
}

type configRequest struct {
	Enabled            bool    `json:"enabled"`
	CountTiers         []int32 `json:"count_tiers"`
	CountOptions       []int32 `json:"countOptions"`
	AmountTiers        []int64 `json:"amount_tiers"`
	AmountOptions      []int64 `json:"amountOptions"`
	DelayedOpenSeconds int32   `json:"delayed_open_seconds"`
	DelaySeconds       int32   `json:"delaySeconds"`
	ExpireSeconds      int32   `json:"expire_seconds"`
	DailySendLimit     int32   `json:"daily_send_limit"`
	DailySendLimitV2   int32   `json:"dailySendLimit"`
	RuleURL            string  `json:"rule_url"`
	RuleURLV2          string  `json:"ruleUrl"`
}

type configDTO struct {
	AppCode            string  `json:"app_code"`
	Enabled            bool    `json:"enabled"`
	CountTiers         []int32 `json:"count_tiers"`
	AmountTiers        []int64 `json:"amount_tiers"`
	DelayedOpenSeconds int32   `json:"delayed_open_seconds"`
	ExpireSeconds      int32   `json:"expire_seconds"`
	DailySendLimit     int32   `json:"daily_send_limit"`
	RuleURL            string  `json:"rule_url"`
	UpdatedByAdminID   int64   `json:"updated_by_admin_id"`
	CreatedAtMS        int64   `json:"created_at_ms"`
	UpdatedAtMS        int64   `json:"updated_at_ms"`
}

type packetDTO struct {
	AppCode         string     `json:"app_code"`
	PacketID        string     `json:"packet_id"`
	CommandID       string     `json:"command_id"`
	SenderUserID    int64      `json:"sender_user_id,string"`
	RoomID          string     `json:"room_id"`
	RegionID        int64      `json:"region_id"`
	PacketType      string     `json:"packet_type"`
	TotalAmount     int64      `json:"total_amount"`
	PacketCount     int32      `json:"packet_count"`
	RemainingAmount int64      `json:"remaining_amount"`
	RemainingCount  int32      `json:"remaining_count"`
	Status          string     `json:"status"`
	OpenAtMS        int64      `json:"open_at_ms"`
	ExpiresAtMS     int64      `json:"expires_at_ms"`
	CreatedAtMS     int64      `json:"created_at_ms"`
	UpdatedAtMS     int64      `json:"updated_at_ms"`
	RefundAmount    int64      `json:"refund_amount"`
	RefundStatus    string     `json:"refund_status"`
	RefundedAtMS    int64      `json:"refunded_at_ms"`
	Claims          []claimDTO `json:"claims,omitempty"`
}

type refundRetryRequest struct {
	PacketNo string `json:"packetNo"`
	PacketID string `json:"packet_id"`
}

type claimDTO struct {
	AppCode             string `json:"app_code"`
	ClaimID             string `json:"claim_id"`
	CommandID           string `json:"command_id"`
	PacketID            string `json:"packet_id"`
	UserID              int64  `json:"user_id,string"`
	Amount              int64  `json:"amount"`
	WalletTransactionID string `json:"wallet_transaction_id"`
	Status              string `json:"status"`
	FailureReason       string `json:"failure_reason"`
	CreatedAtMS         int64  `json:"created_at_ms"`
	UpdatedAtMS         int64  `json:"updated_at_ms"`
}

func (h *Handler) GetConfig(c *gin.Context) {
	if h.wallet == nil {
		response.ServerError(c, "钱包服务未配置")
		return
	}
	resp, err := h.wallet.GetRedPacketConfig(c.Request.Context(), &walletv1.GetRedPacketConfigRequest{
		AppCode: appctx.FromContext(c.Request.Context()),
	})
	if err != nil {
		response.ServerError(c, "获取红包配置失败")
		return
	}
	response.OK(c, configFromProto(resp.GetConfig()))
}

func (h *Handler) UpdateConfig(c *gin.Context) {
	if h.wallet == nil {
		response.ServerError(c, "钱包服务未配置")
		return
	}
	var req configRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "红包配置参数不正确")
		return
	}
	countTiers := req.CountTiers
	if len(countTiers) == 0 {
		countTiers = req.CountOptions
	}
	amountTiers := req.AmountTiers
	if len(amountTiers) == 0 {
		amountTiers = req.AmountOptions
	}
	delaySeconds := req.DelayedOpenSeconds
	if delaySeconds == 0 {
		delaySeconds = req.DelaySeconds
	}
	dailyLimit := req.DailySendLimit
	if dailyLimit == 0 {
		dailyLimit = req.DailySendLimitV2
	}
	ruleURL := strings.TrimSpace(req.RuleURL)
	if ruleURL == "" {
		ruleURL = strings.TrimSpace(req.RuleURLV2)
	}
	resp, err := h.wallet.UpdateRedPacketConfig(c.Request.Context(), &walletv1.UpdateRedPacketConfigRequest{
		AppCode:            appctx.FromContext(c.Request.Context()),
		Enabled:            req.Enabled,
		CountTiers:         countTiers,
		AmountTiers:        amountTiers,
		DelayedOpenSeconds: delaySeconds,
		ExpireSeconds:      86400,
		DailySendLimit:     dailyLimit,
		OperatorUserId:     int64(middleware.CurrentUserID(c)),
		RuleUrl:            ruleURL,
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := configFromProto(resp.GetConfig())
	shared.OperationLogWithResourceID(c, h.audit, "update-red-packet-config", "red_packet_configs", item.AppCode, "success", "")
	response.OK(c, item)
}

func (h *Handler) ListPackets(c *gin.Context) {
	if h.wallet == nil {
		response.ServerError(c, "钱包服务未配置")
		return
	}
	options := shared.ListOptions(c)
	req := &walletv1.ListRedPacketsRequest{
		AppCode:      appctx.FromContext(c.Request.Context()),
		RoomId:       strings.TrimSpace(c.Query("room_id")),
		PacketType:   strings.TrimSpace(c.Query("packet_type")),
		Status:       strings.TrimSpace(options.Status),
		Page:         int32(options.Page),
		PageSize:     int32(options.PageSize),
		SenderUserId: firstInt64Query(c, "senderUserId", "sender_user_id"),
		RegionId:     parseInt64Query(c, "region_id"),
		StartAtMs:    firstInt64Query(c, "startTime", "start_at_ms"),
		EndAtMs:      firstInt64Query(c, "endTime", "end_at_ms"),
	}
	if roomID := strings.TrimSpace(c.Query("roomId")); roomID != "" {
		req.RoomId = roomID
	}
	if packetMode := strings.TrimSpace(c.Query("packetMode")); packetMode != "" {
		req.PacketType = packetMode
	}
	resp, err := h.wallet.ListRedPackets(c.Request.Context(), req)
	if err != nil {
		response.ServerError(c, "获取红包列表失败")
		return
	}
	items := make([]packetDTO, 0, len(resp.GetPackets()))
	for _, packet := range resp.GetPackets() {
		items = append(items, packetFromProto(packet))
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) GetPacket(c *gin.Context) {
	if h.wallet == nil {
		response.ServerError(c, "钱包服务未配置")
		return
	}
	packetID := strings.TrimSpace(c.Param("packet_id"))
	if packetID == "" {
		response.BadRequest(c, "红包 ID 不正确")
		return
	}
	resp, err := h.wallet.GetRedPacket(c.Request.Context(), &walletv1.GetRedPacketRequest{
		AppCode:       appctx.FromContext(c.Request.Context()),
		PacketId:      packetID,
		IncludeClaims: true,
	})
	if err != nil {
		response.ServerError(c, "获取红包详情失败")
		return
	}
	response.OK(c, packetFromProto(resp.GetPacket()))
}

func (h *Handler) RetryRefund(c *gin.Context) {
	if h.wallet == nil {
		response.ServerError(c, "钱包服务未配置")
		return
	}
	var req refundRetryRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "红包退款参数不正确")
		return
	}
	packetID := strings.TrimSpace(req.PacketNo)
	if packetID == "" {
		packetID = strings.TrimSpace(req.PacketID)
	}
	if packetID == "" {
		response.BadRequest(c, "红包 ID 不正确")
		return
	}
	resp, err := h.wallet.RetryRedPacketRefund(c.Request.Context(), &walletv1.RetryRedPacketRefundRequest{
		AppCode:        appctx.FromContext(c.Request.Context()),
		PacketId:       packetID,
		OperatorUserId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	shared.OperationLogWithResourceID(c, h.audit, "retry-red-packet-refund", "red_packets", packetID, "success", "")
	response.OK(c, gin.H{
		"packet":         packetFromProto(resp.GetPacket()),
		"refundedAmount": resp.GetRefundedAmount(),
		"serverTime":     resp.GetServerTimeMs(),
	})
}

func configFromProto(config *walletv1.RedPacketConfig) configDTO {
	if config == nil {
		return configDTO{}
	}
	return configDTO{
		AppCode:            config.GetAppCode(),
		Enabled:            config.GetEnabled(),
		CountTiers:         append([]int32(nil), config.GetCountTiers()...),
		AmountTiers:        append([]int64(nil), config.GetAmountTiers()...),
		DelayedOpenSeconds: config.GetDelayedOpenSeconds(),
		ExpireSeconds:      config.GetExpireSeconds(),
		DailySendLimit:     config.GetDailySendLimit(),
		RuleURL:            config.GetRuleUrl(),
		UpdatedByAdminID:   config.GetUpdatedByAdminId(),
		CreatedAtMS:        config.GetCreatedAtMs(),
		UpdatedAtMS:        config.GetUpdatedAtMs(),
	}
}

func packetFromProto(packet *walletv1.RedPacket) packetDTO {
	if packet == nil {
		return packetDTO{}
	}
	claims := make([]claimDTO, 0, len(packet.GetClaims()))
	for _, claim := range packet.GetClaims() {
		claims = append(claims, claimFromProto(claim))
	}
	return packetDTO{
		AppCode:         packet.GetAppCode(),
		PacketID:        packet.GetPacketId(),
		CommandID:       packet.GetCommandId(),
		SenderUserID:    packet.GetSenderUserId(),
		RoomID:          packet.GetRoomId(),
		RegionID:        packet.GetRegionId(),
		PacketType:      packet.GetPacketType(),
		TotalAmount:     packet.GetTotalAmount(),
		PacketCount:     packet.GetPacketCount(),
		RemainingAmount: packet.GetRemainingAmount(),
		RemainingCount:  packet.GetRemainingCount(),
		Status:          packet.GetStatus(),
		OpenAtMS:        packet.GetOpenAtMs(),
		ExpiresAtMS:     packet.GetExpiresAtMs(),
		CreatedAtMS:     packet.GetCreatedAtMs(),
		UpdatedAtMS:     packet.GetUpdatedAtMs(),
		RefundAmount:    packet.GetRefundAmount(),
		RefundStatus:    packet.GetRefundStatus(),
		RefundedAtMS:    packet.GetRefundedAtMs(),
		Claims:          claims,
	}
}

func claimFromProto(claim *walletv1.RedPacketClaim) claimDTO {
	if claim == nil {
		return claimDTO{}
	}
	return claimDTO{
		AppCode:             claim.GetAppCode(),
		ClaimID:             claim.GetClaimId(),
		CommandID:           claim.GetCommandId(),
		PacketID:            claim.GetPacketId(),
		UserID:              claim.GetUserId(),
		Amount:              claim.GetAmount(),
		WalletTransactionID: claim.GetWalletTransactionId(),
		Status:              claim.GetStatus(),
		FailureReason:       claim.GetFailureReason(),
		CreatedAtMS:         claim.GetCreatedAtMs(),
		UpdatedAtMS:         claim.GetUpdatedAtMs(),
	}
}

func firstInt64Query(c *gin.Context, keys ...string) int64 {
	for _, key := range keys {
		if value := parseInt64Query(c, key); value > 0 {
			return value
		}
	}
	return 0
}

func parseInt64Query(c *gin.Context, key string) int64 {
	value := strings.TrimSpace(c.Query(key))
	if value == "" {
		return 0
	}
	parsed, _ := strconv.ParseInt(value, 10, 64)
	if parsed < 0 {
		return 0
	}
	return parsed
}
