package bscusdt

import (
	"bytes"
	"context"
	"encoding/hex"
	"encoding/json"
	"io"
	"math/big"
	"net/http"
	"strings"
	"time"

	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/config"
)

const transferEventTopic = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

// Client 通过 BSC JSON-RPC 校验 USDT-BEP20 Transfer 事件；它只读取公开 receipt，不触碰私钥。
type Client struct {
	httpClient      *http.Client
	rpcURL          string
	contractAddress string
}

// New 创建 BSC USDT 只读客户端。RPCURL 可以是自建节点、BscScan proxy 或兼容网关。
func New(cfg config.BSCUSDTConfig) *Client {
	timeout := cfg.HTTPTimeout
	if timeout <= 0 {
		timeout = 10 * time.Second
	}
	return &Client{
		httpClient:      &http.Client{Timeout: timeout},
		rpcURL:          strings.TrimSpace(cfg.RPCURL),
		contractAddress: normalizeEVMAddress(cfg.USDTContractAddress),
	}
}

// VerifyUSDTTransfer 校验 tx_hash 已成功执行，且 USDT Transfer 足额转入平台共享地址。
func (c *Client) VerifyUSDTTransfer(ctx context.Context, txHash string, toAddress string, amountMinor int64) (string, error) {
	if c == nil || c.rpcURL == "" {
		return "", xerr.New(xerr.Unavailable, "bsc rpc client is not configured")
	}
	txHash = strings.TrimSpace(txHash)
	toAddress = normalizeEVMAddress(toAddress)
	if txHash == "" || toAddress == "" || amountMinor <= 0 {
		return "", xerr.New(xerr.InvalidArgument, "bsc usdt transfer verification request is incomplete")
	}
	// 订单金额以 USD cent 保存，USDT 使用 6 位精度；1 cent = 10,000 atomic units。
	requiredAtomicAmount := big.NewInt(amountMinor)
	requiredAtomicAmount.Mul(requiredAtomicAmount, big.NewInt(10_000))
	receipt, rawJSON, err := c.transactionReceipt(ctx, txHash)
	if err != nil {
		return rawJSON, err
	}
	if !strings.EqualFold(strings.TrimSpace(receipt.Status), "0x1") {
		return rawJSON, xerr.New(xerr.Conflict, "bsc transaction is not successful")
	}
	for _, log := range receipt.Logs {
		if !strings.EqualFold(normalizeEVMAddress(log.Address), c.contractAddress) {
			continue
		}
		if len(log.Topics) < 3 || !strings.EqualFold(strings.TrimSpace(log.Topics[0]), transferEventTopic) {
			continue
		}
		if topicAddress(log.Topics[2]) != toAddress {
			continue
		}
		amount, ok := parseHexBigInt(log.Data)
		if !ok || amount.Cmp(requiredAtomicAmount) < 0 {
			continue
		}
		return rawJSON, nil
	}
	return rawJSON, xerr.New(xerr.Conflict, "bsc usdt transfer does not match order")
}

func (c *Client) transactionReceipt(ctx context.Context, txHash string) (ethReceipt, string, error) {
	body, err := json.Marshal(jsonRPCRequest{
		JSONRPC: "2.0",
		ID:      1,
		Method:  "eth_getTransactionReceipt",
		Params:  []string{txHash},
	})
	if err != nil {
		return ethReceipt{}, "", err
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.rpcURL, bytes.NewReader(body))
	if err != nil {
		return ethReceipt{}, "", err
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return ethReceipt{}, "", xerr.New(xerr.Unavailable, "bsc rpc request failed")
	}
	defer resp.Body.Close()
	raw, err := io.ReadAll(io.LimitReader(resp.Body, 2<<20))
	if err != nil {
		return ethReceipt{}, "", err
	}
	rawJSON := string(raw)
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return ethReceipt{}, rawJSON, xerr.New(xerr.Unavailable, "bsc rpc returned non-success")
	}
	var parsed struct {
		Result *ethReceipt `json:"result"`
		Error  *struct {
			Code    int64  `json:"code"`
			Message string `json:"message"`
		} `json:"error"`
	}
	if err := json.Unmarshal(raw, &parsed); err != nil {
		return ethReceipt{}, rawJSON, err
	}
	if parsed.Error != nil {
		return ethReceipt{}, rawJSON, xerr.New(xerr.Unavailable, "bsc rpc error: "+strings.TrimSpace(parsed.Error.Message))
	}
	if parsed.Result == nil {
		return ethReceipt{}, rawJSON, xerr.New(xerr.Conflict, "bsc transaction receipt not found")
	}
	return *parsed.Result, rawJSON, nil
}

func normalizeEVMAddress(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	if strings.HasPrefix(value, "0x") && len(value) == 42 {
		return value
	}
	return ""
}

func topicAddress(topic string) string {
	topic = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(topic)), "0x")
	if len(topic) < 40 {
		return ""
	}
	return "0x" + topic[len(topic)-40:]
}

func parseHexBigInt(value string) (*big.Int, bool) {
	value = strings.TrimPrefix(strings.TrimSpace(value), "0x")
	if value == "" {
		return nil, false
	}
	if _, err := hex.DecodeString(padEvenHex(value)); err != nil {
		return nil, false
	}
	amount := new(big.Int)
	if _, ok := amount.SetString(value, 16); !ok {
		return nil, false
	}
	return amount, true
}

func padEvenHex(value string) string {
	if len(value)%2 == 0 {
		return value
	}
	return "0" + value
}

type jsonRPCRequest struct {
	JSONRPC string   `json:"jsonrpc"`
	ID      int64    `json:"id"`
	Method  string   `json:"method"`
	Params  []string `json:"params"`
}

type ethReceipt struct {
	Status string `json:"status"`
	Logs   []struct {
		Address string   `json:"address"`
		Topics  []string `json:"topics"`
		Data    string   `json:"data"`
	} `json:"logs"`
}
