package payment

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

type BEpusdtConfig struct {
	ApiURL    string
	ApiToken  string
	TradeType string
	Fiat      string
	Timeout   int
	Rate      string
}

type BEpusdtClient struct {
	config BEpusdtConfig
	client *http.Client
}

type CreateTransactionRequest struct {
	OrderID     string  `json:"order_id"`
	Amount      float64 `json:"amount"`
	NotifyURL   string  `json:"notify_url"`
	RedirectURL string  `json:"redirect_url"`
	Signature   string  `json:"signature"`
	TradeType   string  `json:"trade_type,omitempty"`
	Fiat        string  `json:"fiat,omitempty"`
	Name        string  `json:"name,omitempty"`
	Timeout     int     `json:"timeout,omitempty"`
	Rate        string  `json:"rate,omitempty"`
	Address     string  `json:"address,omitempty"`
}

type CreateTransactionResponse struct {
	StatusCode int    `json:"status_code"`
	Message    string `json:"message"`
	Data       struct {
		Fiat           string      `json:"fiat"`
		TradeID        string      `json:"trade_id"`
		OrderID        string      `json:"order_id"`
		Amount         interface{} `json:"amount"`
		ActualAmount   interface{} `json:"actual_amount"`
		Status         int         `json:"status"`
		Token          string      `json:"token"`
		ExpirationTime int         `json:"expiration_time"`
		PaymentURL     string      `json:"payment_url"`
	} `json:"data"`
	RequestID string `json:"request_id"`
}

type CallbackData struct {
	TradeID            string  `json:"trade_id"`
	OrderID            string  `json:"order_id"`
	Amount             float64 `json:"amount"`
	ActualAmount       float64 `json:"actual_amount"`
	Token              string  `json:"token"`
	BlockTransactionID string  `json:"block_transaction_id"`
	Signature          string  `json:"signature"`
	Status             int     `json:"status"`
}

func NewBEpusdtClient(config BEpusdtConfig) *BEpusdtClient {
	if config.Fiat == "" {
		config.Fiat = "CNY"
	}
	if config.Timeout == 0 {
		config.Timeout = 600
	}

	return &BEpusdtClient{
		config: config,
		client: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

func (c *BEpusdtClient) GenerateSignature(params map[string]string) string {
	keys := make([]string, 0, len(params))
	for k := range params {
		if params[k] != "" && k != "signature" && k != "sign_type" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)

	var parts []string
	for _, k := range keys {
		parts = append(parts, fmt.Sprintf("%s=%s", k, params[k]))
	}
	signStr := strings.Join(parts, "&") + c.config.ApiToken

	hash := md5.New()
	hash.Write([]byte(signStr))
	return hex.EncodeToString(hash.Sum(nil))
}

func (c *BEpusdtClient) CreateTransaction(orderID string, amount float64, notifyURL, redirectURL, name string) (*CreateTransactionResponse, error) {
	params := map[string]string{
		"order_id":     orderID,
		"amount":       fmt.Sprintf("%.2f", amount),
		"notify_url":   notifyURL,
		"redirect_url": redirectURL,
	}

	if c.config.TradeType != "" {
		params["trade_type"] = c.config.TradeType
	}
	if c.config.Fiat != "" {
		params["fiat"] = c.config.Fiat
	}
	if name != "" {
		params["name"] = name
	}
	if c.config.Timeout > 0 {
		params["timeout"] = fmt.Sprintf("%d", c.config.Timeout)
	}
	if c.config.Rate != "" {
		params["rate"] = c.config.Rate
	}

	params["signature"] = c.GenerateSignature(params)

	jsonData, err := json.Marshal(params)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	apiURL := strings.TrimRight(c.config.ApiURL, "/")
	reqURL := apiURL + "/api/v1/order/create-transaction"

	req, err := http.NewRequest("POST", reqURL, strings.NewReader(string(jsonData)))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response: %w", err)
	}

	var result CreateTransactionResponse
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	if result.StatusCode != 200 {
		return nil, fmt.Errorf("API error: %s (code: %d)", result.Message, result.StatusCode)
	}

	return &result, nil
}

func (c *BEpusdtClient) VerifyCallback(data CallbackData) bool {
	params := map[string]string{
		"trade_id":             data.TradeID,
		"order_id":             data.OrderID,
		"amount":               fmt.Sprintf("%.2f", data.Amount),
		"actual_amount":        fmt.Sprintf("%.2f", data.ActualAmount),
		"token":                data.Token,
		"block_transaction_id": data.BlockTransactionID,
		"status":               fmt.Sprintf("%d", data.Status),
	}

	expectedSign := c.GenerateSignature(params)
	return strings.EqualFold(expectedSign, data.Signature)
}

func ParseCallbackFromQuery(query url.Values) CallbackData {
	var data CallbackData
	data.TradeID = query.Get("trade_id")
	data.OrderID = query.Get("order_id")
	data.Token = query.Get("token")
	data.BlockTransactionID = query.Get("block_transaction_id")
	data.Signature = query.Get("signature")

	if amount := query.Get("amount"); amount != "" {
		fmt.Sscanf(amount, "%f", &data.Amount)
	}
	if actualAmount := query.Get("actual_amount"); actualAmount != "" {
		fmt.Sscanf(actualAmount, "%f", &data.ActualAmount)
	}
	if status := query.Get("status"); status != "" {
		fmt.Sscanf(status, "%d", &data.Status)
	}

	return data
}

func ParseCallbackFromJSON(body []byte) (CallbackData, error) {
	var data CallbackData
	err := json.Unmarshal(body, &data)
	return data, err
}
