package financeorder

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync/atomic"
	"time"

	"hyapp-admin-server/internal/config"
)

var (
	errLegacyCoinSellerNotFound       = errors.New("legacy coin seller not found")
	errLegacyCoinSellerOrderDuplicate = errors.New("legacy coin seller recharge order already exists")
)

const legacyCoinSellerRechargeType = "USDT-进货"

type coinSellerTarget struct {
	UserID        int64
	DisplayUserID string
	CountryID     int64
	CountryCode   string
	RegionID      int64
}

type legacyRechargeGrantInput struct {
	OrderID         uint
	UserID          int64
	ExternalOrderNo string
	ProviderCode    string
	Chain           string
	USDAmount       string
	CoinAmount      int64
	OperatorUserID  uint
	GrantedAt       time.Time
}

type legacyRechargeGrantResult struct {
	TransactionID string
	BalanceAfter  int64
}

// LegacyCoinSellerRechargeWriter 把 admin 订单发放到 Aslan/Yumi 独立 legacy 钱包账套；
// admin 表只保存审计和绑定事实，具体余额/流水仍按旧系统表结构写入。
type LegacyCoinSellerRechargeWriter interface {
	AppCode() string
	Close() error
	ResolveCoinSeller(ctx context.Context, keyword string) (coinSellerTarget, error)
	GrantCoinSellerRecharge(ctx context.Context, input legacyRechargeGrantInput) (legacyRechargeGrantResult, error)
}

type mysqlLegacyCoinSellerRechargeWriter struct {
	appCode        string
	sysOrigin      string
	appDB          *sql.DB
	walletDB       *sql.DB
	requestTimeout time.Duration
}

func NewMySQLLegacyCoinSellerRechargeWriter(source config.LegacyCoinSellerRechargeSourceConfig, appDB *sql.DB, walletDB *sql.DB) LegacyCoinSellerRechargeWriter {
	timeout := source.RequestTimeout
	if timeout <= 0 {
		timeout = 5 * time.Second
	}
	return &mysqlLegacyCoinSellerRechargeWriter{
		appCode:        strings.ToLower(strings.TrimSpace(source.AppCode)),
		sysOrigin:      strings.ToUpper(strings.TrimSpace(source.SysOrigin)),
		appDB:          appDB,
		walletDB:       walletDB,
		requestTimeout: timeout,
	}
}

func (w *mysqlLegacyCoinSellerRechargeWriter) AppCode() string {
	if w == nil {
		return ""
	}
	return w.appCode
}

func (w *mysqlLegacyCoinSellerRechargeWriter) Close() error {
	if w == nil {
		return nil
	}
	var first error
	if w.appDB != nil {
		first = w.appDB.Close()
	}
	if w.walletDB != nil {
		if err := w.walletDB.Close(); first == nil {
			first = err
		}
	}
	return first
}

func (w *mysqlLegacyCoinSellerRechargeWriter) ResolveCoinSeller(ctx context.Context, keyword string) (coinSellerTarget, error) {
	if w == nil || w.appDB == nil || w.walletDB == nil {
		return coinSellerTarget{}, errors.New("legacy coin seller recharge writer is not configured")
	}
	user, err := w.resolveLegacyUser(ctx, keyword)
	if err != nil {
		return coinSellerTarget{}, err
	}
	if err := w.requireLegacyCoinSeller(ctx, user.UserID); err != nil {
		return coinSellerTarget{}, err
	}
	return user, nil
}

func (w *mysqlLegacyCoinSellerRechargeWriter) resolveLegacyUser(ctx context.Context, keyword string) (coinSellerTarget, error) {
	keyword = strings.TrimSpace(keyword)
	if keyword == "" {
		return coinSellerTarget{}, errLegacyCoinSellerNotFound
	}
	queryCtx, cancel := context.WithTimeout(ctx, w.requestTimeout)
	defer cancel()
	args := []any{w.sysOrigin, keyword, keyword}
	matchSQL := "(account = ? OR CAST(id AS CHAR) = ?"
	if numeric, err := strconv.ParseInt(keyword, 10, 64); err == nil && numeric > 0 {
		matchSQL += " OR id = ?"
		args = append(args, numeric)
	}
	matchSQL += ")"
	row := w.appDB.QueryRowContext(queryCtx, `
SELECT id, account, COALESCE(country_id, 0), COALESCE(country_code, '')
FROM user_base_info
WHERE origin_sys = ? AND `+matchSQL+` AND COALESCE(is_del, 0) = 0
ORDER BY id DESC
LIMIT 1`, args...)
	var target coinSellerTarget
	if err := row.Scan(&target.UserID, &target.DisplayUserID, &target.CountryID, &target.CountryCode); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return coinSellerTarget{}, errLegacyCoinSellerNotFound
		}
		return coinSellerTarget{}, err
	}
	target.CountryCode = strings.ToUpper(strings.TrimSpace(target.CountryCode))
	if strings.TrimSpace(target.DisplayUserID) == "" {
		target.DisplayUserID = formatInt64(target.UserID)
	}
	return target, nil
}

func (w *mysqlLegacyCoinSellerRechargeWriter) requireLegacyCoinSeller(ctx context.Context, userID int64) error {
	queryCtx, cancel := context.WithTimeout(ctx, w.requestTimeout)
	defer cancel()
	row := w.walletDB.QueryRowContext(queryCtx, `
SELECT id
FROM user_freight_balance
WHERE sys_origin = ? AND user_id = ? AND COALESCE(is_close, 0) = 0 AND (COALESCE(dealer, 0) = 1 OR COALESCE(super_dealer, 0) = 1)
LIMIT 1`, w.sysOrigin, userID)
	var id int64
	if err := row.Scan(&id); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return errLegacyCoinSellerNotFound
		}
		return err
	}
	return nil
}

func (w *mysqlLegacyCoinSellerRechargeWriter) GrantCoinSellerRecharge(ctx context.Context, input legacyRechargeGrantInput) (legacyRechargeGrantResult, error) {
	if w == nil || w.walletDB == nil {
		return legacyRechargeGrantResult{}, errors.New("legacy coin seller recharge writer is not configured")
	}
	queryCtx, cancel := context.WithTimeout(ctx, w.requestTimeout)
	defer cancel()
	tx, err := w.walletDB.BeginTx(queryCtx, &sql.TxOptions{})
	if err != nil {
		return legacyRechargeGrantResult{}, err
	}
	defer func() {
		_ = tx.Rollback()
	}()
	var (
		balanceID       int64
		earnPointsText  string
		consumptionText string
	)
	if err := tx.QueryRowContext(queryCtx, `
SELECT id, CAST(COALESCE(earn_points, 0) AS CHAR), CAST(COALESCE(consumption_points, 0) AS CHAR)
FROM user_freight_balance
WHERE sys_origin = ? AND user_id = ? AND COALESCE(is_close, 0) = 0 AND (COALESCE(dealer, 0) = 1 OR COALESCE(super_dealer, 0) = 1)
ORDER BY id DESC
LIMIT 1
FOR UPDATE`, w.sysOrigin, input.UserID).Scan(&balanceID, &earnPointsText, &consumptionText); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return legacyRechargeGrantResult{}, errLegacyCoinSellerNotFound
		}
		return legacyRechargeGrantResult{}, err
	}
	var duplicateID int64
	if err := tx.QueryRowContext(queryCtx, `
SELECT id
FROM user_freight_balance_running_water
WHERE sys_origin = ? AND remark = ?
LIMIT 1`, w.sysOrigin, input.ExternalOrderNo).Scan(&duplicateID); err != nil && !errors.Is(err, sql.ErrNoRows) {
		return legacyRechargeGrantResult{}, err
	} else if duplicateID > 0 {
		return legacyRechargeGrantResult{}, errLegacyCoinSellerOrderDuplicate
	}
	if _, err := tx.ExecContext(queryCtx, `
UPDATE user_freight_balance
SET earn_points = COALESCE(earn_points, 0) + ?, update_user = ?, update_time = ?
WHERE id = ?`, input.CoinAmount, input.UserID, input.GrantedAt, balanceID); err != nil {
		return legacyRechargeGrantResult{}, err
	}
	balanceAfter, err := decimalBalanceAfter(earnPointsText, consumptionText, input.CoinAmount)
	if err != nil {
		return legacyRechargeGrantResult{}, err
	}
	waterID := legacySnowflakeLikeID(input.GrantedAt)
	// likei-services 经理中心按固定 recharge_type=USDT-进货 汇总币商入金；
	// V5Pay、Binance 站内转账和链上网络属于支付凭证维度，已完整保存在 admin 订单中，不能写进旧钱包的统计分类字段。
	rechargeType := legacyCoinSellerRechargeType
	if _, err := tx.ExecContext(queryCtx, `
INSERT INTO user_freight_balance_running_water
  (id, sys_origin, user_id, accept_user_id, seller_id, type, quantity, usd_quantity, balance, amount, recharge_type, origin, remark, create_user, update_user, create_time, update_time)
VALUES
  (?, ?, ?, ?, ?, 0, ?, 0, ?, ?, ?, 'PURCHASE', ?, ?, ?, ?, ?)`,
		waterID,
		w.sysOrigin,
		input.UserID,
		input.UserID,
		nil,
		input.CoinAmount,
		balanceAfter,
		input.USDAmount,
		rechargeType,
		input.ExternalOrderNo,
		input.UserID,
		input.UserID,
		input.GrantedAt,
		input.GrantedAt,
	); err != nil {
		return legacyRechargeGrantResult{}, err
	}
	if err := tx.Commit(); err != nil {
		return legacyRechargeGrantResult{}, err
	}
	return legacyRechargeGrantResult{
		TransactionID: fmt.Sprintf("legacy-freight-purchase-%d", waterID),
		BalanceAfter:  balanceAfter,
	}, nil
}

func decimalBalanceAfter(earnPointsText string, consumptionText string, delta int64) (int64, error) {
	earn, err := strconv.ParseFloat(strings.TrimSpace(earnPointsText), 64)
	if err != nil {
		return 0, err
	}
	consumption, err := strconv.ParseFloat(strings.TrimSpace(consumptionText), 64)
	if err != nil {
		return 0, err
	}
	return int64(earn) + delta - int64(consumption), nil
}

func legacySnowflakeLikeID(now time.Time) int64 {
	// 旧服务用 MyBatis ASSIGN_ID 写 bigint 主键；后台没有接入旧雪花 worker，只需保证单进程内足够唯一且单调。
	return now.UTC().UnixMilli()*1000 + int64(legacyWaterIDCounter.Add(1)%1000)
}

var legacyWaterIDCounter atomic.Uint32
