// Package mysqltest provides real wallet-service MySQL repositories for tests.
package mysqltest

import (
	"context"
	"fmt"
	"testing"
	"time"

	"hyapp/internal/testutil/mysqlschema"
	mysqlstorage "hyapp/services/wallet-service/internal/storage/mysql"
)

// Repository wraps the production MySQL wallet repository with test seed helpers.
type Repository struct {
	*mysqlstorage.Repository

	t      testing.TB
	schema *mysqlschema.Schema
}

// CountRows returns a simple count from a known wallet table for persistence assertions.
func (r *Repository) CountRows(table string, where string, args ...any) int {
	r.t.Helper()

	query := "SELECT COUNT(*) FROM " + validateTableName(table)
	if where != "" {
		query += " WHERE " + where
	}
	var count int
	if err := r.schema.DB.QueryRowContext(context.Background(), query, args...).Scan(&count); err != nil {
		r.t.Fatalf("count %s rows failed: %v\nquery=%s", table, err, query)
	}
	return count
}

// InsertRawGiftPrice allows tests to seed inactive or future prices without service helpers.
func (r *Repository) InsertRawGiftPrice(giftID string, priceVersion string, status string, effectiveAtMs int64) {
	r.t.Helper()

	nowMs := time.Now().UnixMilli()
	_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO wallet_gift_prices (
			gift_id, price_version, status, coin_price, gift_point_amount, heat_value,
			effective_at_ms, created_at_ms, updated_at_ms
		) VALUES (?, ?, ?, 1, 1, 1, ?, ?, ?)
	`, giftID, priceVersion, status, effectiveAtMs, nowMs, nowMs)
	if err != nil {
		r.t.Fatalf("insert raw gift price failed: %v", err)
	}
}

// NewRepository creates an isolated wallet schema and opens the production repository.
func NewRepository(t testing.TB) *Repository {
	t.Helper()

	schema := mysqlschema.New(t, mysqlschema.Config{
		EnvVar:         "WALLET_SERVICE_MYSQL_TEST_DSN",
		InitDBPath:     initDBPath(t),
		DatabasePrefix: "hyapp_wallet_test",
	})
	repository, err := mysqlstorage.Open(context.Background(), schema.DSN)
	if err != nil {
		t.Fatalf("open wallet mysql repository failed: %v", err)
	}

	wrapped := &Repository{Repository: repository, t: t, schema: schema}
	t.Cleanup(wrapped.Close)
	return wrapped
}

// Close shuts down the repository; mysqlschema owns schema cleanup.
func (r *Repository) Close() {
	r.t.Helper()

	if r.Repository != nil {
		_ = r.Repository.Close()
	}
}

// SetBalance prepares a sender COIN account for debit tests.
func (r *Repository) SetBalance(userID int64, balance int64) {
	r.SetAssetBalance(userID, "COIN", balance)
}

// SetAssetBalance prepares a sender account for debit tests.
func (r *Repository) SetAssetBalance(userID int64, assetType string, balance int64) {
	r.t.Helper()

	nowMs := time.Now().UnixMilli()
	_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO wallet_accounts (user_id, asset_type, available_amount, frozen_amount, version, created_at_ms, updated_at_ms)
		VALUES (?, ?, ?, 0, 1, ?, ?)
		ON DUPLICATE KEY UPDATE
			available_amount = VALUES(available_amount),
			frozen_amount = VALUES(frozen_amount),
			version = version + 1,
			updated_at_ms = VALUES(updated_at_ms)
	`, userID, assetType, balance, nowMs, nowMs)
	if err != nil {
		r.t.Fatalf("seed wallet balance failed: %v", err)
	}
}

// SetGiftPrice overrides or inserts a gift settlement price for server-side billing tests.
func (r *Repository) SetGiftPrice(giftID string, priceVersion string, coinPrice int64, giftPointAmount int64, heatValue int64) {
	r.t.Helper()

	nowMs := time.Now().UnixMilli()
	_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO wallet_gift_prices (
			gift_id, price_version, status, coin_price, gift_point_amount, heat_value,
			effective_at_ms, created_at_ms, updated_at_ms
		) VALUES (?, ?, 'active', ?, ?, ?, 0, ?, ?)
		ON DUPLICATE KEY UPDATE
			status = VALUES(status),
			coin_price = VALUES(coin_price),
			gift_point_amount = VALUES(gift_point_amount),
			heat_value = VALUES(heat_value),
			updated_at_ms = VALUES(updated_at_ms)
	`, giftID, priceVersion, coinPrice, giftPointAmount, heatValue, nowMs, nowMs)
	if err != nil {
		r.t.Fatalf("seed wallet gift price failed: %v", err)
	}
}

// SetRechargePolicy 配置区域充值汇率，用于币商转账充值口径测试。
func (r *Repository) SetRechargePolicy(regionID int64, policyVersion string, coinAmount int64, usdMinorAmount int64) {
	r.t.Helper()

	nowMs := time.Now().UnixMilli()
	_, err := r.schema.DB.ExecContext(context.Background(), `
		INSERT INTO wallet_recharge_policies (
			region_id, policy_version, status, currency_code, coin_amount, usd_minor_amount,
			effective_from_ms, created_at_ms, updated_at_ms
		) VALUES (?, ?, 'active', 'USD', ?, ?, 0, ?, ?)
		ON DUPLICATE KEY UPDATE
			status = VALUES(status),
			currency_code = VALUES(currency_code),
			coin_amount = VALUES(coin_amount),
			usd_minor_amount = VALUES(usd_minor_amount),
			updated_at_ms = VALUES(updated_at_ms)
	`, regionID, policyVersion, coinAmount, usdMinorAmount, nowMs, nowMs)
	if err != nil {
		r.t.Fatalf("seed wallet recharge policy failed: %v", err)
	}
}

func initDBPath(t testing.TB) string {
	t.Helper()

	return mysqlschema.InitDBPath(t,
		mysqlschema.CallerFile(t, 1),
		"..", "..", "..", "deploy", "mysql", "initdb", "001_wallet_service.sql",
	)
}

func validateTableName(table string) string {
	switch table {
	case "wallet_transactions", "wallet_entries", "wallet_outbox", "wallet_accounts", "wallet_recharge_records",
		"coin_seller_stock_records", "wallet_diamond_exchange_rules", "wallet_withdrawal_requests",
		"vip_levels", "user_vip_memberships", "vip_purchase_orders", "user_vip_history",
		"resources", "resource_groups", "resource_group_items", "gift_configs", "gift_config_regions", "resource_grants", "resource_grant_items", "user_resource_entitlements", "user_resource_equipment":
		return table
	default:
		panic(fmt.Sprintf("unsupported wallet test table %q", table))
	}
}
