package trongrid

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

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

// Client 校验用户提交的 USDT-TRC20 tx_hash；它只读链上公开数据，不保存也不接触任何私钥。
type Client struct {
	httpClient      *http.Client
	apiBaseURL      string
	apiKey          string
	contractAddress string
}

// New 创建 TronGrid 兼容客户端；base_url 可以指向官方 TronGrid、自建节点代理或兼容网关。
func New(cfg config.TronGridConfig) *Client {
	timeout := cfg.HTTPTimeout
	if timeout <= 0 {
		timeout = 10 * time.Second
	}
	return &Client{
		httpClient: &http.Client{Timeout: timeout},
		apiBaseURL: strings.TrimRight(strings.TrimSpace(cfg.APIBaseURL), "/"),
		apiKey:     strings.TrimSpace(cfg.APIKey),
		// Base58Check 区分大小写；保存原始形式，比较时再统一解码为账户 Hex。
		contractAddress: strings.TrimSpace(cfg.USDTContractAddress),
	}
}

// VerifyUSDTTransfer 校验 tx_hash 指向的 confirmed Transfer 是否把足额 USDT 打到平台共享地址。
func (c *Client) VerifyUSDTTransfer(ctx context.Context, txHash string, toAddress string, amountMinor int64) (string, error) {
	if c == nil || c.apiBaseURL == "" {
		return "", xerr.New(xerr.Unavailable, "tron grid client is not configured")
	}
	txHash = strings.TrimSpace(txHash)
	if txHash == "" || toAddress == "" || amountMinor <= 0 {
		return "", xerr.New(xerr.InvalidArgument, "usdt transfer verification request is incomplete")
	}
	expectedAddress, ok := normalizeTronAddress(toAddress)
	if !ok {
		return "", xerr.New(xerr.InvalidArgument, "tron receive address is invalid")
	}
	// H5 商品金额以 USD cent 保存，USDT 使用 6 位小数；1 cent = 0.01 USDT = 10,000 atomic units。
	requiredAtomicAmount := amountMinor * 10_000
	events, rawJSON, err := c.listTransferEvents(ctx, txHash)
	if err != nil {
		return "", err
	}
	for _, event := range events {
		if !strings.EqualFold(event.EventName, "Transfer") {
			continue
		}
		if !c.contractMatches(event.ContractAddress) {
			continue
		}
		// TronGrid 的 event result.to 可能返回 0x/41 Hex，而后台保存的是 Base58Check。
		// 两端先归一为去除 TRON 网络前缀的 20-byte Hex，避免同一地址因编码形式不同被误判。
		eventAddress, validAddress := normalizeTronAddress(event.Result.To)
		if !validAddress || eventAddress != expectedAddress {
			continue
		}
		value, err := strconv.ParseInt(strings.TrimSpace(event.Result.Value), 10, 64)
		if err != nil || value < requiredAtomicAmount {
			continue
		}
		return rawJSON, nil
	}
	return rawJSON, xerr.New(xerr.Conflict, "usdt transfer is not confirmed or does not match order")
}

func (c *Client) listTransferEvents(ctx context.Context, txHash string) ([]transferEvent, string, error) {
	endpoint := c.apiBaseURL + "/v1/transactions/" + url.PathEscape(txHash) + "/events?event_name=Transfer&only_confirmed=true&limit=50"
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, "", err
	}
	if c.apiKey != "" {
		req.Header.Set("TRON-PRO-API-KEY", c.apiKey)
	}
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, "", xerr.New(xerr.Unavailable, "tron grid request failed")
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(io.LimitReader(resp.Body, 2<<20))
	if err != nil {
		return nil, "", err
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, string(body), xerr.New(xerr.Unavailable, "tron grid returned non-success")
	}
	var parsed struct {
		Data []transferEvent `json:"data"`
	}
	if err := json.Unmarshal(body, &parsed); err != nil {
		return nil, string(body), err
	}
	return parsed.Data, string(body), nil
}

func (c *Client) contractMatches(value string) bool {
	configured, configuredOK := normalizeTronAddress(c.contractAddress)
	actual, actualOK := normalizeTronAddress(value)
	return configuredOK && actualOK && configured == actual
}

const tronBase58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

// normalizeTronAddress 接受后台常用的 Base58Check，以及 TronGrid 可能返回的
// 0x + 20-byte、41 + 20-byte 或裸 20-byte Hex，统一输出账户主体的 40 位小写 Hex。
// Base58 输入必须通过网络前缀和双 SHA-256 checksum 校验，避免把格式正确但已损坏的地址用于放币判断。
func normalizeTronAddress(value string) (string, bool) {
	value = strings.TrimSpace(value)
	if value == "" {
		return "", false
	}
	hexValue := strings.TrimPrefix(strings.ToLower(value), "0x")
	if len(hexValue) == 42 && strings.HasPrefix(hexValue, "41") {
		hexValue = hexValue[2:]
	}
	if len(hexValue) == 40 {
		if _, err := hex.DecodeString(hexValue); err == nil {
			return hexValue, true
		}
	}
	payload, ok := decodeTronBase58Check(value)
	if !ok || len(payload) != 21 || payload[0] != 0x41 {
		return "", false
	}
	return hex.EncodeToString(payload[1:]), true
}

func decodeTronBase58Check(value string) ([]byte, bool) {
	number := new(big.Int)
	base := big.NewInt(58)
	for _, char := range value {
		index := strings.IndexRune(tronBase58Alphabet, char)
		if index < 0 {
			return nil, false
		}
		number.Mul(number, base)
		number.Add(number, big.NewInt(int64(index)))
	}
	decoded := number.Bytes()
	leadingZeroes := 0
	for leadingZeroes < len(value) && value[leadingZeroes] == '1' {
		leadingZeroes++
	}
	if leadingZeroes > 0 {
		decoded = append(make([]byte, leadingZeroes), decoded...)
	}
	if len(decoded) < 5 {
		return nil, false
	}
	payload, checksum := decoded[:len(decoded)-4], decoded[len(decoded)-4:]
	firstHash := sha256.Sum256(payload)
	secondHash := sha256.Sum256(firstHash[:])
	if !bytes.Equal(checksum, secondHash[:4]) {
		return nil, false
	}
	return payload, true
}

type transferEvent struct {
	EventName       string `json:"event_name"`
	ContractAddress string `json:"contract_address"`
	Result          struct {
		To    string `json:"to"`
		Value string `json:"value"`
	} `json:"result"`
}
