package cumulativerechargereward

import (
	"context"
	"database/sql"
	"strings"
	"time"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/activityclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/response"
	activityv1 "hyapp.local/api/proto/activity/v1"

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

type Handler struct {
	activity activityclient.Client
	userDB   *sql.DB
	audit    shared.OperationLogger
}

func New(activity activityclient.Client, userDB *sql.DB, audit shared.OperationLogger) *Handler {
	return &Handler{activity: activity, userDB: userDB, audit: audit}
}

type configRequest struct {
	Enabled bool      `json:"enabled"`
	Tiers   []tierDTO `json:"tiers"`
}

type configDTO struct {
	AppCode          string    `json:"app_code"`
	Enabled          bool      `json:"enabled"`
	Tiers            []tierDTO `json:"tiers"`
	UpdatedByAdminID int64     `json:"updated_by_admin_id"`
	CreatedAtMS      int64     `json:"created_at_ms"`
	UpdatedAtMS      int64     `json:"updated_at_ms"`
}

type tierDTO struct {
	TierID            int64  `json:"tier_id"`
	TierCode          string `json:"tier_code"`
	TierName          string `json:"tier_name"`
	ThresholdUSDMinor int64  `json:"threshold_usd_minor"`
	ResourceGroupID   int64  `json:"resource_group_id"`
	Status            string `json:"status"`
	SortOrder         int32  `json:"sort_order"`
	CreatedAtMS       int64  `json:"created_at_ms"`
	UpdatedAtMS       int64  `json:"updated_at_ms"`
}

type grantDTO struct {
	GrantID            string   `json:"grant_id"`
	AppCode            string   `json:"app_code"`
	CycleKey           string   `json:"cycle_key"`
	UserID             int64    `json:"user_id,string"`
	User               *userDTO `json:"user,omitempty"`
	EventID            string   `json:"event_id"`
	TransactionID      string   `json:"transaction_id"`
	CommandID          string   `json:"command_id"`
	TierID             int64    `json:"tier_id"`
	TierCode           string   `json:"tier_code"`
	ThresholdUSDMinor  int64    `json:"threshold_usd_minor"`
	ResourceGroupID    int64    `json:"resource_group_id"`
	ReachedUSDMinor    int64    `json:"reached_usd_minor"`
	QualifyingUSDMinor int64    `json:"qualifying_usd_minor"`
	RechargeCoinAmount int64    `json:"recharge_coin_amount"`
	RechargeType       string   `json:"recharge_type"`
	Status             string   `json:"status"`
	WalletCommandID    string   `json:"wallet_command_id"`
	WalletGrantID      string   `json:"wallet_grant_id"`
	FailureReason      string   `json:"failure_reason"`
	GrantedAtMS        int64    `json:"granted_at_ms"`
	CreatedAtMS        int64    `json:"created_at_ms"`
	UpdatedAtMS        int64    `json:"updated_at_ms"`
}

type userDTO struct {
	UserID        int64  `json:"user_id,string"`
	DisplayUserID string `json:"display_user_id"`
	Username      string `json:"username"`
	Avatar        string `json:"avatar"`
}

func (h *Handler) GetConfig(c *gin.Context) {
	resp, err := h.activity.GetCumulativeRechargeRewardConfig(c.Request.Context(), &activityv1.GetCumulativeRechargeRewardConfigRequest{Meta: h.meta(c)})
	if err != nil {
		response.ServerError(c, "获取累充奖励配置失败")
		return
	}
	response.OK(c, configFromProto(resp.GetConfig()))
}

func (h *Handler) UpdateConfig(c *gin.Context) {
	var req configRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "累充奖励配置参数不正确")
		return
	}
	// admin-server 只做输入透传和操作人注入，档位有效性、重复门槛和启用规则统一交给 activity-service 校验。
	tiers := make([]*activityv1.CumulativeRechargeRewardTier, 0, len(req.Tiers))
	for _, tier := range req.Tiers {
		tiers = append(tiers, &activityv1.CumulativeRechargeRewardTier{
			TierId:            tier.TierID,
			TierCode:          strings.TrimSpace(tier.TierCode),
			TierName:          strings.TrimSpace(tier.TierName),
			ThresholdUsdMinor: tier.ThresholdUSDMinor,
			ResourceGroupId:   tier.ResourceGroupID,
			Status:            strings.TrimSpace(tier.Status),
			SortOrder:         tier.SortOrder,
		})
	}
	resp, err := h.activity.UpdateCumulativeRechargeRewardConfig(c.Request.Context(), &activityv1.UpdateCumulativeRechargeRewardConfigRequest{
		Meta:            h.meta(c),
		Enabled:         req.Enabled,
		Tiers:           tiers,
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := configFromProto(resp.GetConfig())
	shared.OperationLogWithResourceID(c, h.audit, "update-cumulative-recharge-reward", "cumulative_recharge_reward_configs", item.AppCode, "success", "")
	response.OK(c, item)
}

func (h *Handler) ListGrants(c *gin.Context) {
	options := shared.ListOptions(c)
	// 发放记录主表只存 user_id；后台 keyword 先在 users 表解析成精确用户，再下推给 activity-service 查询。
	userFilter := shared.UserIdentityFilterFromQuery(c, "")
	userID, matched, ok := h.resolveGrantUserID(c.Request.Context(), strings.TrimSpace(options.Keyword), userFilter)
	if !ok {
		response.ServerError(c, "查询用户信息失败")
		return
	}
	if (options.Keyword != "" || !userFilter.IsEmpty()) && !matched {
		response.OK(c, response.Page{Items: []grantDTO{}, Page: options.Page, PageSize: options.PageSize, Total: 0})
		return
	}
	resp, err := h.activity.ListCumulativeRechargeRewardGrants(c.Request.Context(), &activityv1.ListCumulativeRechargeRewardGrantsRequest{
		Meta:     h.meta(c),
		Status:   strings.TrimSpace(options.Status),
		UserId:   userID,
		CycleKey: strings.TrimSpace(c.Query("cycle_key")),
		Page:     int32(options.Page),
		PageSize: int32(options.PageSize),
	})
	if err != nil {
		response.ServerError(c, "获取累充奖励发放记录失败")
		return
	}
	items := make([]grantDTO, 0, len(resp.GetGrants()))
	for _, grant := range resp.GetGrants() {
		items = append(items, grantFromProto(grant))
	}
	if err := h.fillGrantUsers(c.Request.Context(), items); err != nil {
		response.ServerError(c, "补全用户信息失败")
		return
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta {
	return &activityv1.RequestMeta{
		RequestId: middleware.CurrentRequestID(c),
		Caller:    "admin-server",
		AppCode:   appctx.FromContext(c.Request.Context()),
		SentAtMs:  time.Now().UnixMilli(),
	}
}

func (h *Handler) resolveGrantUserID(ctx context.Context, keyword string, filter shared.UserIdentityFilter) (int64, bool, bool) {
	if filter.IsEmpty() && keyword == "" {
		return 0, false, true
	}
	if !filter.IsEmpty() {
		userID, matched, err := shared.ResolveUserIdentityFilter(ctx, h.userDB, appctx.FromContext(ctx), filter, time.Now().UnixMilli())
		return userID, matched, err == nil
	}
	// 累计充值奖励按 activity-service 的内部 user_id 过滤；keyword 的长 ID、短号、靓号、昵称优先级由 shared 统一维护。
	userID, matched, err := shared.ResolveExactUserID(ctx, h.userDB, appctx.FromContext(ctx), keyword, time.Now().UnixMilli())
	if err != nil {
		return 0, false, false
	}
	return userID, matched, true
}

func (h *Handler) fillGrantUsers(ctx context.Context, grants []grantDTO) error {
	if h.userDB == nil || len(grants) == 0 {
		return nil
	}
	// 列表补用户资料是展示增强，找不到用户时仍返回 grant 原始 user_id，避免审计记录因为用户资料缺失而不可见。
	ids := collectGrantUserIDs(grants)
	if len(ids) == 0 {
		return nil
	}
	args := append([]any{appctx.FromContext(ctx)}, int64Args(ids)...)
	rows, err := h.userDB.QueryContext(ctx, `
		SELECT user_id, current_display_user_id, COALESCE(username, ''), COALESCE(avatar, '')
		FROM users
		WHERE app_code = ? AND user_id IN (`+placeholders(len(ids))+`)`,
		args...,
	)
	if err != nil {
		return err
	}
	defer rows.Close()
	users := make(map[int64]userDTO, len(ids))
	for rows.Next() {
		var user userDTO
		if err := rows.Scan(&user.UserID, &user.DisplayUserID, &user.Username, &user.Avatar); err != nil {
			return err
		}
		users[user.UserID] = user
	}
	if err := rows.Err(); err != nil {
		return err
	}
	for index := range grants {
		if user, ok := users[grants[index].UserID]; ok {
			grants[index].User = &user
			continue
		}
		grants[index].User = &userDTO{UserID: grants[index].UserID}
	}
	return nil
}

func configFromProto(config *activityv1.CumulativeRechargeRewardConfig) configDTO {
	if config == nil {
		return configDTO{Tiers: []tierDTO{}}
	}
	tiers := make([]tierDTO, 0, len(config.GetTiers()))
	for _, tier := range config.GetTiers() {
		tiers = append(tiers, tierFromProto(tier))
	}
	return configDTO{
		AppCode:          config.GetAppCode(),
		Enabled:          config.GetEnabled(),
		Tiers:            tiers,
		UpdatedByAdminID: config.GetUpdatedByAdminId(),
		CreatedAtMS:      config.GetCreatedAtMs(),
		UpdatedAtMS:      config.GetUpdatedAtMs(),
	}
}

func tierFromProto(tier *activityv1.CumulativeRechargeRewardTier) tierDTO {
	if tier == nil {
		return tierDTO{}
	}
	return tierDTO{
		TierID:            tier.GetTierId(),
		TierCode:          tier.GetTierCode(),
		TierName:          tier.GetTierName(),
		ThresholdUSDMinor: tier.GetThresholdUsdMinor(),
		ResourceGroupID:   tier.GetResourceGroupId(),
		Status:            tier.GetStatus(),
		SortOrder:         tier.GetSortOrder(),
		CreatedAtMS:       tier.GetCreatedAtMs(),
		UpdatedAtMS:       tier.GetUpdatedAtMs(),
	}
}

func grantFromProto(grant *activityv1.CumulativeRechargeRewardGrant) grantDTO {
	if grant == nil {
		return grantDTO{}
	}
	return grantDTO{
		GrantID:            grant.GetGrantId(),
		AppCode:            grant.GetAppCode(),
		CycleKey:           grant.GetCycleKey(),
		UserID:             grant.GetUserId(),
		EventID:            grant.GetEventId(),
		TransactionID:      grant.GetTransactionId(),
		CommandID:          grant.GetCommandId(),
		TierID:             grant.GetTierId(),
		TierCode:           grant.GetTierCode(),
		ThresholdUSDMinor:  grant.GetThresholdUsdMinor(),
		ResourceGroupID:    grant.GetResourceGroupId(),
		ReachedUSDMinor:    grant.GetReachedUsdMinor(),
		QualifyingUSDMinor: grant.GetQualifyingUsdMinor(),
		RechargeCoinAmount: grant.GetRechargeCoinAmount(),
		RechargeType:       grant.GetRechargeType(),
		Status:             grant.GetStatus(),
		WalletCommandID:    grant.GetWalletCommandId(),
		WalletGrantID:      grant.GetWalletGrantId(),
		FailureReason:      grant.GetFailureReason(),
		GrantedAtMS:        grant.GetGrantedAtMs(),
		CreatedAtMS:        grant.GetCreatedAtMs(),
		UpdatedAtMS:        grant.GetUpdatedAtMs(),
	}
}

func collectGrantUserIDs(grants []grantDTO) []int64 {
	seen := make(map[int64]struct{}, len(grants))
	ids := make([]int64, 0, len(grants))
	for _, grant := range grants {
		if grant.UserID <= 0 {
			continue
		}
		if _, ok := seen[grant.UserID]; ok {
			continue
		}
		seen[grant.UserID] = struct{}{}
		ids = append(ids, grant.UserID)
	}
	return ids
}

func int64Args(ids []int64) []any {
	args := make([]any, 0, len(ids))
	for _, id := range ids {
		args = append(args, id)
	}
	return args
}

func placeholders(count int) string {
	if count <= 0 {
		return ""
	}
	parts := make([]string, count)
	for i := range parts {
		parts[i] = "?"
	}
	return strings.Join(parts, ",")
}
