package apple

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"fmt"
	"net/http"
	"time"
)

type ServerAPIConfig struct {
	KeyID      string
	IssuerID   string
	PrivateKey string
	Sandbox    bool
	BundleID   string
}

func buildAPIToken(cfg ServerAPIConfig) (string, error) {
	header := map[string]interface{}{
		"alg": "ES256",
		"kid": cfg.KeyID,
		"typ": "JWT",
	}
	now := time.Now().Unix()
	payload := map[string]interface{}{
		"iss": cfg.IssuerID,
		"iat": now,
		"exp": now + 1800,
		"aud": "appstoreconnect-v1",
	}
	if cfg.BundleID != "" {
		payload["bid"] = cfg.BundleID
	}
	hb, _ := json.Marshal(header)
	pb, _ := json.Marshal(payload)
	enc := func(b []byte) string {
		return base64.RawURLEncoding.EncodeToString(b)
	}
	unsigned := fmt.Sprintf("%s.%s", enc(hb), enc(pb))
	block, _ := pem.Decode([]byte(cfg.PrivateKey))
	if block == nil {
		return "", fmt.Errorf("invalid private key")
	}
	keyAny, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return "", err
	}
	priv, ok := keyAny.(*ecdsa.PrivateKey)
	if !ok {
		return "", fmt.Errorf("private key is not ECDSA")
	}
	// Correctly calculate R and S for ES256 (P-256 + SHA-256)
	digest := sha256Sum([]byte(unsigned))
	r, s, err := ecdsa.Sign(rand.Reader, priv, digest)
	if err != nil {
		return "", err
	}
	// Concatenate R and S (each 32 bytes for P-256)
	curveBits := priv.Curve.Params().BitSize
	keyBytes := curveBits / 8
	if curveBits%8 > 0 {
		keyBytes += 1
	}
	rBytes := r.Bytes()
	rBytesPadded := make([]byte, keyBytes)
	copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)

	sBytes := s.Bytes()
	sBytesPadded := make([]byte, keyBytes)
	copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)

	sig := append(rBytesPadded, sBytesPadded...)
	return unsigned + "." + base64.RawURLEncoding.EncodeToString(sig), nil
}

func sha256Sum(b []byte) []byte {
	h := sha256.New()
	h.Write(b)
	return h.Sum(nil)
}

func GetTransactionInfo(cfg ServerAPIConfig, transactionId string) (string, error) {
	token, err := buildAPIToken(cfg)
	if err != nil {
		return "", err
	}
	try := func(host string) (string, int, string, error) {
		url := fmt.Sprintf("%s/inApps/v1/transactions/%s", host, transactionId)
		req, _ := http.NewRequest("GET", url, nil)
		req.Header.Set("Authorization", "Bearer "+token)
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			return "", 0, "", err
		}
		defer resp.Body.Close()
		buf := new(bytes.Buffer)
		_, _ = buf.ReadFrom(resp.Body)
		if resp.StatusCode != 200 {
			return "", resp.StatusCode, buf.String(), fmt.Errorf("apple api error: %d", resp.StatusCode)
		}
		var body struct {
			SignedTransactionInfo string `json:"signedTransactionInfo"`
		}
		if err := json.Unmarshal(buf.Bytes(), &body); err != nil {
			return "", resp.StatusCode, buf.String(), err
		}
		return body.SignedTransactionInfo, resp.StatusCode, buf.String(), nil
	}
	primary := "https://api.storekit.itunes.apple.com"
	secondary := "https://api.storekit-sandbox.itunes.apple.com"
	if cfg.Sandbox {
		primary, secondary = secondary, primary
	}
	jws, code, body, err := try(primary)
	if err == nil && jws != "" {
		return jws, nil
	}
	// Fallback to the other environment if primary failed (common when env mismatches)
	jws2, code2, body2, err2 := try(secondary)
	if err2 == nil && jws2 != "" {
		return jws2, nil
	}
	return "", fmt.Errorf("apple api error, primary[%d:%s], secondary[%d:%s]", code, body, code2, body2)
}

// GetTransactionHistory 获取指定交易 ID 的完整交易历史（该用户的所有交易）
// Apple API: GET /inApps/v2/history/{transactionId}
// 返回所有交易的 JWS 列表
func GetTransactionHistory(cfg ServerAPIConfig, transactionId string) ([]string, error) {
	token, err := buildAPIToken(cfg)
	if err != nil {
		return nil, err
	}

	fetchPage := func(host, revision string) ([]string, string, bool, int, error) {
		url := fmt.Sprintf("%s/inApps/v2/history/%s?sort=DESCENDING", host, transactionId)
		if revision != "" {
			url += "&revision=" + revision
		}
		req, _ := http.NewRequest("GET", url, nil)
		req.Header.Set("Authorization", "Bearer "+token)
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			return nil, "", false, 0, err
		}
		defer resp.Body.Close()
		buf := new(bytes.Buffer)
		_, _ = buf.ReadFrom(resp.Body)
		if resp.StatusCode != 200 {
			return nil, "", false, resp.StatusCode, fmt.Errorf("apple api error: %d %s", resp.StatusCode, buf.String())
		}
		var result struct {
			SignedTransactions []string `json:"signedTransactions"`
			Revision          string   `json:"revision"`
			HasMore           bool     `json:"hasMore"`
		}
		if err := json.Unmarshal(buf.Bytes(), &result); err != nil {
			return nil, "", false, resp.StatusCode, err
		}
		return result.SignedTransactions, result.Revision, result.HasMore, resp.StatusCode, nil
	}

	primary := "https://api.storekit.itunes.apple.com"
	secondary := "https://api.storekit-sandbox.itunes.apple.com"
	if cfg.Sandbox {
		primary, secondary = secondary, primary
	}

	// 尝试 primary 环境
	var allTx []string
	revision := ""
	for {
		txs, rev, hasMore, _, err := fetchPage(primary, revision)
		if err != nil {
			break // primary 失败，尝试 secondary
		}
		allTx = append(allTx, txs...)
		if !hasMore || rev == "" {
			return allTx, nil
		}
		revision = rev
	}

	// Fallback: secondary 环境
	allTx = nil
	revision = ""
	for {
		txs, rev, hasMore, code, err := fetchPage(secondary, revision)
		if err != nil {
			return nil, fmt.Errorf("both environments failed, secondary[%d]: %v", code, err)
		}
		allTx = append(allTx, txs...)
		if !hasMore || rev == "" {
			return allTx, nil
		}
		revision = rev
	}
}

