package services

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

const (
	BaseURL   = "https://sandboxapi.rapyd.net"
	SecretKey = "rsk_a97addbe02a32a153eaedf1db35c11ce24e4b6c7257c01eb5953d59ad5d281d8f1b1c6a60b68b"
	AccessKey = "rak_EC66D246035CA48157FA"
)

func generateSalt(length int) string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[rand.Intn(len(charset))]
	}
	return string(b)
}

func getUnixTime() int64 {
	return 1747194248
}

func createSignature(method, urlPath string, body []byte, salt string, timestamp int64) string {
	bodyStr := string(body)

	toSign := strings.ToLower(method) +
		urlPath +
		salt +
		strconv.FormatInt(timestamp, 10) +
		AccessKey +
		bodyStr

	fmt.Println("Data to sign (Go):", toSign)

	h := hmac.New(sha256.New, []byte(SecretKey))
	h.Write([]byte(toSign))

	// Elimina padding '=' al final
	encoder := base64.URLEncoding.WithPadding(base64.NoPadding)
	signature := encoder.EncodeToString(h.Sum(nil))

	fmt.Printf("HMAC (Go hex): %x\n", h.Sum(nil))
	fmt.Println("Signature (Go):", signature)

	return signature
}

func createHeaders(method, fullURL string, body interface{}) ([]byte, map[string]string, error) {
	// Serializar cuerpo como JSON sin espacios
	var bodyBytes []byte
	var err error
	if body != nil {
		bodyBytes, err = json.Marshal(body)
		if err != nil {
			return nil, nil, err
		}
	} else {
		bodyBytes = []byte{}
	}

	// Obtener solo la ruta `/v1/...` desde fullURL
	u, err := url.Parse(fullURL)
	if err != nil {
		return nil, nil, err
	}
	urlPath := u.Path

	//salt := generateSalt(12)
	salt := "fixed_salt_12"
	timestamp := getUnixTime()
	signature := createSignature(method, urlPath, bodyBytes, salt, timestamp)

	headers := map[string]string{
		"access_key":   AccessKey,
		"salt":         salt,
		"timestamp":    strconv.FormatInt(timestamp, 10),
		"signature":    signature,
		"idempotency":  strconv.FormatInt(time.Now().UnixNano(), 10),
		"Content-Type": "application/json",
	}

	return bodyBytes, headers, nil
}

func MakeRequest(method, path string, body interface{}) (map[string]interface{}, error) {
	fullURL := BaseURL + path
	urlPath := path

	// Serializa el cuerpo sin espacios ni reordenamientos
	bodyBytes, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}

	salt := "fixed_salt_12"
	timestamp := int64(1747194248) // Fijo para comparar con Python
	signature := createSignature(method, urlPath, bodyBytes, salt, timestamp)

	headers := map[string]string{
		"access_key":   AccessKey,
		"salt":         salt,
		"timestamp":    strconv.FormatInt(timestamp, 10),
		"signature":    signature,
		"idempotency":  strconv.FormatInt(time.Now().UnixNano(), 10),
		"Content-Type": "application/json",
	}

	// 👇 IMPRIMIR TODO LO QUE SE ENVÍA 👇
	fmt.Println("➡️ REQUEST ENVIADO:")
	fmt.Println("Method:", method)
	fmt.Println("URL:", fullURL)
	fmt.Println("Headers:")
	for k, v := range headers {
		fmt.Printf("  %s: %s\n", k, v)
	}
	fmt.Println("Body:")
	fmt.Println(string(bodyBytes))
	fmt.Println(strings.Repeat("-", 50))

	// Crea request
	req, err := http.NewRequest(strings.ToUpper(method), fullURL, bytes.NewReader(bodyBytes))
	if err != nil {
		return nil, err
	}

	for k, v := range headers {
		req.Header.Set(k, v)
	}

	// 👇 OPCIONAL: Imprimir curl equivalente 👇
	curlCmd := fmt.Sprintf("curl -X %s '%s' \\\n", req.Method, req.URL.String())
	for k, v := range req.Header {
		curlCmd += fmt.Sprintf("  -H '%s: %s' \\\n", k, v[0])
	}
	if len(bodyBytes) > 0 {
		curlCmd += fmt.Sprintf("  -d '%s'", string(bodyBytes))
	}
	fmt.Println("📋 Comando curl equivalente:")
	fmt.Println(curlCmd)
	fmt.Println(strings.Repeat("-", 50))

	// Envía la solicitud
	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("status %d: %s", resp.StatusCode, string(respBody))
	}

	var result map[string]interface{}
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, err
	}

	return result, nil
}
