package mysql

import (
	"context"
	"database/sql"
	"errors"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
	resourcedomain "hyapp/services/wallet-service/internal/domain/resource"
	"strings"
)

func normalizeGiftChargeSource(source string, entitlementID string) string {
	normalized := strings.ToLower(strings.TrimSpace(source))
	if normalized == giftChargeSourceBag || strings.TrimSpace(entitlementID) != "" {
		return giftChargeSourceBag
	}
	return giftChargeSourceCoin
}

func (r *Repository) consumeGiftEntitlementForSend(ctx context.Context, tx *sql.Tx, command ledger.DebitGiftCommand, giftResourceID int64, quantity int64, metadata giftMetadata, nowMs int64) (walletOutboxEvent, error) {
	entitlementID := strings.TrimSpace(command.EntitlementID)
	if entitlementID == "" {
		return walletOutboxEvent{}, xerr.New(xerr.InvalidArgument, "entitlement_id is required")
	}
	if command.SenderUserID <= 0 || giftResourceID <= 0 || quantity <= 0 {
		return walletOutboxEvent{}, xerr.New(xerr.InvalidArgument, "bag gift entitlement command is invalid")
	}
	row := tx.QueryRowContext(ctx, userResourceSelectSQL()+`
		WHERE e.app_code = ? AND e.entitlement_id = ?
		FOR UPDATE`,
		appcode.FromContext(ctx), entitlementID,
	)
	entitlement, err := scanUserResourceEntitlement(row)
	if errors.Is(err, sql.ErrNoRows) {
		return walletOutboxEvent{}, xerr.New(xerr.NotFound, "bag gift entitlement not found")
	}
	if err != nil {
		return walletOutboxEvent{}, err
	}
	if entitlement.UserID != command.SenderUserID {
		return walletOutboxEvent{}, xerr.New(xerr.PermissionDenied, "bag gift entitlement does not belong to sender")
	}
	if entitlement.ResourceID != giftResourceID {
		return walletOutboxEvent{}, xerr.New(xerr.InvalidArgument, "bag gift entitlement resource mismatch")
	}
	if entitlement.Status != resourcedomain.StatusActive || entitlement.Resource.Status != resourcedomain.StatusActive {
		return walletOutboxEvent{}, xerr.New(xerr.Conflict, "bag gift entitlement is not active")
	}
	if resourcedomain.NormalizeResourceType(entitlement.Resource.ResourceType) != resourcedomain.TypeGift {
		return walletOutboxEvent{}, xerr.New(xerr.InvalidArgument, "bag entitlement resource_type must be gift")
	}
	if entitlement.EffectiveAtMS > nowMs || (entitlement.ExpiresAtMS > 0 && entitlement.ExpiresAtMS <= nowMs) {
		return walletOutboxEvent{}, xerr.New(xerr.Conflict, "bag gift entitlement is expired")
	}
	if entitlement.RemainingQuantity < quantity {
		return walletOutboxEvent{}, xerr.New(xerr.InsufficientBalance, "bag gift inventory is insufficient")
	}
	result, err := tx.ExecContext(ctx, `
		UPDATE user_resource_entitlements
		SET remaining_quantity = remaining_quantity - ?, updated_at_ms = ?
		WHERE app_code = ? AND entitlement_id = ? AND remaining_quantity >= ?`,
		quantity, nowMs, appcode.FromContext(ctx), entitlementID, quantity,
	)
	if err != nil {
		return walletOutboxEvent{}, err
	}
	affected, err := result.RowsAffected()
	if err != nil {
		return walletOutboxEvent{}, err
	}
	if affected != 1 {

		return walletOutboxEvent{}, xerr.New(xerr.InsufficientBalance, "bag gift inventory is insufficient")
	}
	entitlement.RemainingQuantity -= quantity
	entitlement.UpdatedAtMS = nowMs
	return resourceOutboxEvent("UserResourceChanged", command.CommandID, command.SenderUserID, giftResourceID, map[string]any{
		"action":             "consume_gift_send",
		"app_code":           appcode.FromContext(ctx),
		"user_id":            command.SenderUserID,
		"resource_id":        giftResourceID,
		"resource_type":      entitlement.Resource.ResourceType,
		"entitlement_id":     entitlementID,
		"consumed_quantity":  quantity,
		"remaining_quantity": entitlement.RemainingQuantity,
		"room_id":            command.RoomID,
		"gift_id":            command.GiftID,
		"charge_source":      giftChargeSourceBag,
		"gift_value_coins":   metadata.CoinSpent,
		"updated_at_ms":      nowMs,
		"entitlement":        entitlement,
	}, nowMs), nil
}
