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"
	"time"
)

func (r *Repository) upsertGiftPriceTx(ctx context.Context, tx *sql.Tx, command resourcedomain.GiftConfigCommand, nowMs int64) error {
	legacyHeatValue := command.CoinPrice
	_, err := tx.ExecContext(ctx, `
		INSERT INTO wallet_gift_prices (
		   app_code, gift_id, price_version, status, charge_asset_type, coin_price, gift_point_amount,
		   heat_value, effective_at_ms, created_at_ms, updated_at_ms
		 ) VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
		   status = VALUES(status),
		   charge_asset_type = VALUES(charge_asset_type),
		   coin_price = VALUES(coin_price),
		   gift_point_amount = VALUES(gift_point_amount),
		   heat_value = VALUES(heat_value),
		   effective_at_ms = VALUES(effective_at_ms),
		   updated_at_ms = VALUES(updated_at_ms)`,
		appcode.FromContext(ctx), command.GiftID, command.PriceVersion, command.ChargeAssetType, command.CoinPrice,
		command.GiftPointAmount, legacyHeatValue, command.EffectiveAtMS, nowMs, nowMs,
	)
	return err
}

func (r *Repository) latestGiftPrice(ctx context.Context, querier sqlRowQuerier, giftID string, nowMs int64) (giftPrice, error) {
	row := querier.QueryRowContext(ctx,
		`SELECT gift_id, price_version, COALESCE(charge_asset_type, 'COIN'), coin_price, gift_point_amount, heat_value
		 FROM wallet_gift_prices
		 WHERE app_code = ? AND gift_id = ? AND status = 'active' AND effective_at_ms <= ?
		 ORDER BY effective_at_ms DESC, price_version DESC
		 LIMIT 1`,
		appcode.FromContext(ctx), giftID, nowMs,
	)
	var price giftPrice
	if err := row.Scan(&price.GiftID, &price.PriceVersion, &price.ChargeAssetType, &price.CoinPrice, &price.GiftPointAmount, &price.HeatValue); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return giftPrice{}, xerr.New(xerr.NotFound, "gift price is not active")
		}
		return giftPrice{}, err
	}
	price.ChargeAssetType = ledger.NormalizeGiftChargeAssetType(price.ChargeAssetType)
	return price, nil
}

func (r *Repository) latestGiftPrices(ctx context.Context, querier sqlQuerier, giftIDs []string, nowMs int64) (map[string]giftPrice, error) {
	pricesByGiftID := make(map[string]giftPrice, len(giftIDs))
	if len(giftIDs) == 0 {
		return pricesByGiftID, nil
	}
	args := append([]any{appcode.FromContext(ctx)}, stringAnyArgs(giftIDs)...)
	args = append(args, nowMs, nowMs)
	rows, err := querier.QueryContext(ctx, `
			SELECT gp.gift_id, gp.price_version, COALESCE(gp.charge_asset_type, 'COIN'), gp.coin_price, gp.gift_point_amount, gp.heat_value
			FROM wallet_gift_prices gp
			WHERE gp.app_code = ? AND gp.gift_id IN (`+placeholders(len(giftIDs))+`)
			  AND gp.status = 'active' AND gp.effective_at_ms <= ?
			  AND NOT EXISTS (
			    SELECT 1
			    FROM wallet_gift_prices newer
			    WHERE newer.app_code = gp.app_code
			      AND newer.gift_id = gp.gift_id
			      AND newer.status = 'active'
			      AND newer.effective_at_ms <= ?
			      AND (
			        newer.effective_at_ms > gp.effective_at_ms
			        OR (newer.effective_at_ms = gp.effective_at_ms AND newer.price_version > gp.price_version)
			      )
			  )
			ORDER BY gp.gift_id ASC`,
		args...,
	)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	for rows.Next() {
		var price giftPrice
		if err := rows.Scan(&price.GiftID, &price.PriceVersion, &price.ChargeAssetType, &price.CoinPrice, &price.GiftPointAmount, &price.HeatValue); err != nil {
			return nil, err
		}
		price.ChargeAssetType = ledger.NormalizeGiftChargeAssetType(price.ChargeAssetType)
		pricesByGiftID[price.GiftID] = price
	}
	return pricesByGiftID, rows.Err()
}

func applyGiftPriceToConfig(gift *resourcedomain.GiftConfig, price giftPrice) {
	if gift == nil {
		return
	}
	gift.PriceVersion = price.PriceVersion
	gift.ChargeAssetType = price.ChargeAssetType
	gift.CoinPrice = price.CoinPrice
	// GIFT_POINT 已下线；列表继续把历史价格行里的 gift_point_amount 压成 0，避免旧数据重新暴露给客户端。
	gift.GiftPointAmount = 0
	// 历史列表口径把热度值等同于金币价格，即使价格表里仍保留 heat_value，也不在展示面板引入第二套价值口径。
	gift.HeatValue = price.CoinPrice
}

func (r *Repository) resolveActiveGiftConfig(ctx context.Context, tx *sql.Tx, giftID string, regionID int64) (resourcedomain.GiftConfig, error) {
	nowMs := time.Now().UnixMilli()
	row := tx.QueryRowContext(ctx, giftConfigSelectSQL()+`
		WHERE gc.app_code = ? AND gc.gift_id = ? AND gc.status = 'active' AND r.status = 'active' AND r.resource_type = 'gift'
		  AND (COALESCE(gc.effective_from_ms, 0) = 0 OR gc.effective_from_ms <= ?)
		  AND (COALESCE(gc.effective_to_ms, 0) = 0 OR gc.effective_to_ms > ?)
		  AND EXISTS (
		    SELECT 1
		    FROM gift_config_regions gcr
		    WHERE gcr.app_code = gc.app_code
		      AND gcr.gift_id = gc.gift_id
		      AND (gcr.region_id = 0 OR gcr.region_id = ?)
		  )
		FOR UPDATE`,
		appcode.FromContext(ctx), giftID, nowMs, nowMs, regionID,
	)
	gift, err := scanGiftConfig(row)
	if errors.Is(err, sql.ErrNoRows) {
		return resourcedomain.GiftConfig{}, xerr.New(xerr.NotFound, "gift config is not active")
	}
	if err != nil {
		return resourcedomain.GiftConfig{}, err
	}
	gift.RegionIDs, err = r.listGiftConfigRegionIDs(ctx, tx, gift.GiftID)
	return gift, err
}

func applyResourcePricingToGiftCommand(command resourcedomain.GiftConfigCommand, resource resourcedomain.Resource) resourcedomain.GiftConfigCommand {
	switch resourcedomain.NormalizePriceType(resource.PriceType) {
	case resourcedomain.PriceTypeCoin:
		command.ChargeAssetType = ledger.AssetCoin
		command.CoinPrice = resource.CoinPrice
		command.GiftPointAmount = 0
	case resourcedomain.PriceTypeFree:
		command.ChargeAssetType = ledger.AssetCoin
		command.CoinPrice = 0
		command.GiftPointAmount = 0
	}

	command.HeatValue = command.CoinPrice
	return command
}
