package mysql

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
	"strings"
	"time"
)

// GrantVip 是活动和后台赠送 VIP 的统一账务入口；它不扣金币，也不检查充值门槛。
func (r *Repository) GrantVip(ctx context.Context, command ledger.GrantVipCommand) (ledger.GrantVipReceipt, error) {
	if r == nil || r.db == nil {
		return ledger.GrantVipReceipt{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	ctx = contextWithCommandApp(ctx, command.AppCode)
	command.AppCode = appcode.FromContext(ctx)
	command.GrantSource = ledger.NormalizeVipGrantSource(command.GrantSource)
	if command.GrantSource == "" || command.GrantSource == ledger.VipGrantSourcePurchase {
		return ledger.GrantVipReceipt{}, xerr.New(xerr.InvalidArgument, "vip grant_source is invalid")
	}
	command.Reason = strings.TrimSpace(command.Reason)
	if command.Reason == "" {
		command.Reason = command.GrantSource
	}

	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	defer func() { _ = tx.Rollback() }()

	requestHash := vipGrantRequestHash(command)
	if txRow, exists, err := r.lookupTransaction(ctx, tx, command.CommandID, requestHash, bizTypeVIPGrant); err != nil || exists {
		if err != nil || !exists {
			return ledger.GrantVipReceipt{}, err
		}
		return r.vipGrantReceiptForTransaction(ctx, tx, txRow, command.TargetUserID)
	}

	nowMs := time.Now().UnixMilli()
	level, err := r.getVipLevelForUpdate(ctx, tx, command.Level)
	if err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	if level.Status != ledger.VipStatusActive {
		return ledger.GrantVipReceipt{}, xerr.New(xerr.VIPLevelDisabled, "vip level is disabled")
	}
	if command.GrantSource == ledger.VipGrantSourceManagerCenter {

		level.DurationMS = managerCenterVIPGrantDurationMS
	}
	current, err := r.ensureUserVipForUpdate(ctx, tx, command.TargetUserID, nowMs)
	if err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	transactionID := transactionID(command.AppCode, command.CommandID)
	recordID := "vip_grant_" + stableHash(command.AppCode+"|"+command.CommandID)
	expectedResourceGrantID := resourceGrantID(command.AppCode, "vip_reward:"+command.CommandID)
	metadata := map[string]any{
		"app_code":                   command.AppCode,
		"grant_id":                   recordID,
		"resource_grant_id":          expectedResourceGrantID,
		"user_id":                    command.TargetUserID,
		"target_level":               command.Level,
		"grant_source":               command.GrantSource,
		"operator_user_id":           command.OperatorUserID,
		"reason":                     command.Reason,
		"reward_group_id":            level.RewardResourceGroupID,
		"duration_ms":                level.DurationMS,
		"transaction_id":             transactionID,
		"required_recharge_bypassed": true,
	}
	if err := r.insertTransaction(ctx, tx, transactionID, command.CommandID, bizTypeVIPGrant, requestHash, recordID, metadata, nowMs); err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	activation, err := r.activateUserVip(ctx, tx, vipActivationCommand{
		AppCode:        command.AppCode,
		CommandID:      command.CommandID,
		UserID:         command.TargetUserID,
		Source:         command.GrantSource,
		Reason:         command.Reason,
		OperatorUserID: command.OperatorUserID,
		TransactionID:  transactionID,
		RecordID:       recordID,
		Level:          level,
		Current:        current,
		NowMS:          nowMs,
	})
	if err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	if err := r.insertWalletOutbox(ctx, tx, []walletOutboxEvent{
		vipActivatedEvent(transactionID, command.CommandID, command.TargetUserID, command.GrantSource, activation.Action, activation.PreviousLevel, activation.VIP, level.RewardResourceGroupID, activation.ResourceGrantID, activation.RewardItems, nowMs),
	}); err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	if err := tx.Commit(); err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	return ledger.GrantVipReceipt{
		TransactionID: transactionID,
		Vip:           activation.VIP,
		RewardItems:   activation.RewardItems,
	}, nil
}

func (r *Repository) vipGrantReceiptForTransaction(ctx context.Context, tx *sql.Tx, txRow transactionRow, userID int64) (ledger.GrantVipReceipt, error) {
	var metadata struct {
		ResourceGrantID string `json:"resource_grant_id"`
	}
	_ = json.Unmarshal([]byte(txRow.MetadataJSON), &metadata)
	vip, err := r.queryUserVip(ctx, tx, userID, time.Now().UnixMilli(), false)
	if err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	rewards, err := r.rewardItemsByGrant(ctx, tx, metadata.ResourceGrantID, vip.ExpiresAtMS)
	if err != nil {
		return ledger.GrantVipReceipt{}, err
	}
	return ledger.GrantVipReceipt{
		TransactionID: txRow.TransactionID,
		Vip:           vip,
		RewardItems:   rewards,
	}, nil
}

func vipGrantRequestHash(command ledger.GrantVipCommand) string {
	return stableHash(fmt.Sprintf("vip_grant|%s|%d|%d|%s|%d|%s",
		appcode.Normalize(command.AppCode),
		command.TargetUserID,
		command.Level,
		ledger.NormalizeVipGrantSource(command.GrantSource),
		command.OperatorUserID,
		strings.TrimSpace(command.Reason),
	))
}
