// Package privacy provides privacy-enhancing cryptographic features.
package privacy

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync/atomic"
	"time"
)

const DefaultEndpoint = "https://privacy.synor.cc/api/v1"

// Config is the privacy client configuration.
type Config struct {
	APIKey   string
	Endpoint string
	Timeout  time.Duration
	Retries  int
	Debug    bool
}

// KeyType represents the type of cryptographic key.
type KeyType string

const (
	KeyTypeEd25519   KeyType = "ed25519"
	KeyTypeSecp256k1 KeyType = "secp256k1"
)

// GeneratorType represents the generator used for commitments.
type GeneratorType string

const (
	GeneratorDefault   GeneratorType = "default"
	GeneratorAlternate GeneratorType = "alternate"
)

// PublicKey represents a public key.
type PublicKey struct {
	Key  string  `json:"key"`
	Type KeyType `json:"type"`
}

// PrivateKey represents a private key.
type PrivateKey struct {
	Key  string  `json:"key"`
	Type KeyType `json:"type"`
}

// ConfidentialUTXO is a UTXO for confidential transactions.
type ConfidentialUTXO struct {
	TxID       string `json:"txid"`
	Vout       int    `json:"vout"`
	Commitment string `json:"commitment"`
	RangeProof string `json:"rangeProof"`
	Blinding   string `json:"blinding,omitempty"`
	Amount     string `json:"amount,omitempty"`
}

// ConfidentialOutput is an output for confidential transactions.
type ConfidentialOutput struct {
	Recipient string `json:"recipient"`
	Amount    string `json:"amount"`
	Blinding  string `json:"blinding,omitempty"`
}

// OutputFeatures contains output feature flags.
type OutputFeatures struct {
	Flags      int `json:"flags"`
	LockHeight int `json:"lockHeight,omitempty"`
}

// ConfidentialTxInput is a confidential transaction input.
type ConfidentialTxInput struct {
	OutputRef  string `json:"outputRef"`
	Commitment string `json:"commitment"`
}

// ConfidentialTxOutput is a confidential transaction output.
type ConfidentialTxOutput struct {
	Commitment string         `json:"commitment"`
	RangeProof string         `json:"rangeProof"`
	Features   OutputFeatures `json:"features"`
}

// TransactionKernel is the kernel for confidential transactions.
type TransactionKernel struct {
	Features        int    `json:"features"`
	Fee             string `json:"fee"`
	LockHeight      int    `json:"lockHeight"`
	Excess          string `json:"excess"`
	ExcessSignature string `json:"excessSignature"`
}

// ConfidentialTransaction is a confidential transaction.
type ConfidentialTransaction struct {
	TxID    string                 `json:"txid"`
	Version int                    `json:"version"`
	Inputs  []ConfidentialTxInput  `json:"inputs"`
	Outputs []ConfidentialTxOutput `json:"outputs"`
	Kernel  TransactionKernel      `json:"kernel"`
	Offset  string                 `json:"offset"`
	Raw     string                 `json:"raw"`
}

// VerifyConfidentialTxDetails contains verification details.
type VerifyConfidentialTxDetails struct {
	CommitmentsBalance  bool `json:"commitmentsBalance"`
	RangeProofsValid    bool `json:"rangeProofsValid"`
	SignatureValid      bool `json:"signatureValid"`
	NoDuplicateInputs   bool `json:"noDuplicateInputs"`
}

// VerifyConfidentialTxResult is the result of verifying a confidential transaction.
type VerifyConfidentialTxResult struct {
	Valid   bool                        `json:"valid"`
	Details VerifyConfidentialTxDetails `json:"details"`
	Error   string                      `json:"error,omitempty"`
}

// RingSignatureComponents contains the signature components.
type RingSignatureComponents struct {
	C []string `json:"c"`
	R []string `json:"r"`
}

// RingSignature is a ring signature.
type RingSignature struct {
	ID          string                  `json:"id"`
	MessageHash string                  `json:"messageHash"`
	Ring        []string                `json:"ring"`
	KeyImage    string                  `json:"keyImage"`
	Signature   RingSignatureComponents `json:"signature"`
	RingSize    int                     `json:"ringSize"`
}

// VerifyRingSignatureResult is the result of verifying a ring signature.
type VerifyRingSignatureResult struct {
	Valid    bool   `json:"valid"`
	KeyImage string `json:"keyImage"`
}

// StealthAddress is a stealth address.
type StealthAddress struct {
	Address        string `json:"address"`
	ScanPublicKey  string `json:"scanPublicKey"`
	SpendPublicKey string `json:"spendPublicKey"`
}

// StealthKeypair is a stealth address keypair.
type StealthKeypair struct {
	Address         StealthAddress `json:"address"`
	ScanPrivateKey  string         `json:"scanPrivateKey"`
	SpendPrivateKey string         `json:"spendPrivateKey"`
}

// OneTimeAddress is a one-time address derived from a stealth address.
type OneTimeAddress struct {
	Address            string `json:"address"`
	EphemeralPublicKey string `json:"ephemeralPublicKey"`
	SharedSecret       string `json:"sharedSecret,omitempty"`
}

// SharedSecret is the result of deriving a shared secret.
type SharedSecret struct {
	Secret            string `json:"secret"`
	OneTimePrivateKey string `json:"oneTimePrivateKey"`
	OneTimeAddress    string `json:"oneTimeAddress"`
}

// Commitment is a Pedersen commitment.
type Commitment struct {
	Commitment string        `json:"commitment"`
	Generator  GeneratorType `json:"generator"`
}

// CommitmentWithBlinding is a commitment with its blinding factor.
type CommitmentWithBlinding struct {
	Commitment Commitment `json:"commitment"`
	Blinding   string     `json:"blinding"`
}

// RangeProof is a Bulletproof range proof.
type RangeProof struct {
	Proof      string `json:"proof"`
	Commitment string `json:"commitment"`
	BitLength  int    `json:"bitLength"`
}

// VerifyRangeProofResult is the result of verifying a range proof.
type VerifyRangeProofResult struct {
	Valid    bool   `json:"valid"`
	MinValue string `json:"minValue"`
	MaxValue string `json:"maxValue"`
}

// Error is a privacy error.
type Error struct {
	Message    string
	Code       string
	StatusCode int
}

func (e *Error) Error() string {
	return e.Message
}

// Client is a Synor Privacy client.
type Client struct {
	config     Config
	httpClient *http.Client
	closed     atomic.Bool
}

// NewClient creates a new privacy client.
func NewClient(config Config) *Client {
	if config.Endpoint == "" {
		config.Endpoint = DefaultEndpoint
	}
	if config.Timeout == 0 {
		config.Timeout = 30 * time.Second
	}
	if config.Retries == 0 {
		config.Retries = 3
	}

	return &Client{
		config: config,
		httpClient: &http.Client{
			Timeout: config.Timeout,
		},
	}
}

// ==================== Confidential Transactions ====================

// CreateConfidentialTxOptions are options for creating a confidential transaction.
type CreateConfidentialTxOptions struct {
	Inputs     []ConfidentialUTXO
	Outputs    []ConfidentialOutput
	Fee        string
	LockHeight int
}

// CreateConfidentialTransaction creates a confidential transaction.
func (c *Client) CreateConfidentialTransaction(ctx context.Context, opts CreateConfidentialTxOptions) (*ConfidentialTransaction, error) {
	body := map[string]interface{}{
		"inputs":  opts.Inputs,
		"outputs": opts.Outputs,
	}
	if opts.Fee != "" {
		body["fee"] = opts.Fee
	}
	if opts.LockHeight > 0 {
		body["lockHeight"] = opts.LockHeight
	}

	var resp struct {
		Transaction ConfidentialTransaction `json:"transaction"`
	}
	if err := c.request(ctx, "POST", "/transactions/confidential", body, &resp); err != nil {
		return nil, err
	}
	return &resp.Transaction, nil
}

// VerifyConfidentialTransaction verifies a confidential transaction.
func (c *Client) VerifyConfidentialTransaction(ctx context.Context, tx *ConfidentialTransaction) (*VerifyConfidentialTxResult, error) {
	body := map[string]interface{}{
		"transaction": tx.Raw,
	}
	var result VerifyConfidentialTxResult
	if err := c.request(ctx, "POST", "/transactions/confidential/verify", body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// DecodeConfidentialOutput decodes a confidential output.
func (c *Client) DecodeConfidentialOutput(ctx context.Context, commitment, blinding string) (string, error) {
	body := map[string]interface{}{
		"commitment": commitment,
		"blinding":   blinding,
	}
	var resp struct {
		Amount string `json:"amount"`
	}
	if err := c.request(ctx, "POST", "/transactions/confidential/decode", body, &resp); err != nil {
		return "", err
	}
	return resp.Amount, nil
}

// ==================== Ring Signatures ====================

// CreateRingSignatureOptions are options for creating a ring signature.
type CreateRingSignatureOptions struct {
	Message     string
	Ring        []string
	PrivateKey  string
	SignerIndex *int
}

// CreateRingSignature creates a ring signature.
func (c *Client) CreateRingSignature(ctx context.Context, opts CreateRingSignatureOptions) (*RingSignature, error) {
	body := map[string]interface{}{
		"message":    opts.Message,
		"ring":       opts.Ring,
		"privateKey": opts.PrivateKey,
	}
	if opts.SignerIndex != nil {
		body["signerIndex"] = *opts.SignerIndex
	}

	var resp struct {
		Signature RingSignature `json:"signature"`
	}
	if err := c.request(ctx, "POST", "/ring-signatures/create", body, &resp); err != nil {
		return nil, err
	}
	return &resp.Signature, nil
}

// VerifyRingSignature verifies a ring signature.
func (c *Client) VerifyRingSignature(ctx context.Context, signature *RingSignature, message string) (*VerifyRingSignatureResult, error) {
	body := map[string]interface{}{
		"signature": signature,
		"message":   message,
	}
	var result VerifyRingSignatureResult
	if err := c.request(ctx, "POST", "/ring-signatures/verify", body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// IsKeyImageUsed checks if a key image has been used.
func (c *Client) IsKeyImageUsed(ctx context.Context, keyImage string) (bool, error) {
	var resp struct {
		Used bool `json:"used"`
	}
	if err := c.request(ctx, "GET", fmt.Sprintf("/ring-signatures/key-images/%s", keyImage), nil, &resp); err != nil {
		return false, err
	}
	return resp.Used, nil
}

// GenerateRandomRing generates a random ring of public keys.
func (c *Client) GenerateRandomRing(ctx context.Context, size int, exclude []string) ([]string, error) {
	body := map[string]interface{}{
		"size": size,
	}
	if len(exclude) > 0 {
		body["exclude"] = exclude
	}
	var resp struct {
		Ring []string `json:"ring"`
	}
	if err := c.request(ctx, "POST", "/ring-signatures/random-ring", body, &resp); err != nil {
		return nil, err
	}
	return resp.Ring, nil
}

// ==================== Stealth Addresses ====================

// GenerateStealthKeypair generates a new stealth address keypair.
func (c *Client) GenerateStealthKeypair(ctx context.Context) (*StealthKeypair, error) {
	var resp struct {
		Keypair StealthKeypair `json:"keypair"`
	}
	if err := c.request(ctx, "POST", "/stealth/generate", nil, &resp); err != nil {
		return nil, err
	}
	return &resp.Keypair, nil
}

// CreateOneTimeAddress creates a one-time address for a stealth address.
func (c *Client) CreateOneTimeAddress(ctx context.Context, stealthAddress *StealthAddress) (*OneTimeAddress, error) {
	body := map[string]interface{}{
		"scanPublicKey":  stealthAddress.ScanPublicKey,
		"spendPublicKey": stealthAddress.SpendPublicKey,
	}
	var resp struct {
		OneTimeAddress OneTimeAddress `json:"oneTimeAddress"`
	}
	if err := c.request(ctx, "POST", "/stealth/one-time-address", body, &resp); err != nil {
		return nil, err
	}
	return &resp.OneTimeAddress, nil
}

// DeriveSharedSecret derives the shared secret for a stealth payment.
func (c *Client) DeriveSharedSecret(ctx context.Context, stealthAddress *StealthAddress, privateKey, ephemeralPublicKey string) (*SharedSecret, error) {
	body := map[string]interface{}{
		"scanPublicKey":      stealthAddress.ScanPublicKey,
		"spendPublicKey":     stealthAddress.SpendPublicKey,
		"privateKey":         privateKey,
		"ephemeralPublicKey": ephemeralPublicKey,
	}
	var secret SharedSecret
	if err := c.request(ctx, "POST", "/stealth/derive-secret", body, &secret); err != nil {
		return nil, err
	}
	return &secret, nil
}

// ScanForPayments scans transactions for stealth payments.
func (c *Client) ScanForPayments(ctx context.Context, scanPrivateKey, spendPublicKey string, transactions []string) ([]OneTimeAddress, error) {
	body := map[string]interface{}{
		"scanPrivateKey": scanPrivateKey,
		"spendPublicKey": spendPublicKey,
		"transactions":   transactions,
	}
	var resp struct {
		Payments []OneTimeAddress `json:"payments"`
	}
	if err := c.request(ctx, "POST", "/stealth/scan", body, &resp); err != nil {
		return nil, err
	}
	return resp.Payments, nil
}

// ==================== Commitments ====================

// CreateCommitment creates a Pedersen commitment.
func (c *Client) CreateCommitment(ctx context.Context, value string, blinding string) (*CommitmentWithBlinding, error) {
	body := map[string]interface{}{
		"value": value,
	}
	if blinding != "" {
		body["blinding"] = blinding
	}
	var result CommitmentWithBlinding
	if err := c.request(ctx, "POST", "/commitments/create", body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// OpenCommitment verifies a Pedersen commitment.
func (c *Client) OpenCommitment(ctx context.Context, commitment, value, blinding string) (bool, error) {
	body := map[string]interface{}{
		"commitment": commitment,
		"value":      value,
		"blinding":   blinding,
	}
	var resp struct {
		Valid bool `json:"valid"`
	}
	if err := c.request(ctx, "POST", "/commitments/open", body, &resp); err != nil {
		return false, err
	}
	return resp.Valid, nil
}

// AddCommitments adds two commitments.
func (c *Client) AddCommitments(ctx context.Context, commitment1, commitment2 string) (string, error) {
	body := map[string]interface{}{
		"commitment1": commitment1,
		"commitment2": commitment2,
	}
	var resp struct {
		Commitment string `json:"commitment"`
	}
	if err := c.request(ctx, "POST", "/commitments/add", body, &resp); err != nil {
		return "", err
	}
	return resp.Commitment, nil
}

// SubtractCommitments subtracts two commitments.
func (c *Client) SubtractCommitments(ctx context.Context, commitment1, commitment2 string) (string, error) {
	body := map[string]interface{}{
		"commitment1": commitment1,
		"commitment2": commitment2,
	}
	var resp struct {
		Commitment string `json:"commitment"`
	}
	if err := c.request(ctx, "POST", "/commitments/subtract", body, &resp); err != nil {
		return "", err
	}
	return resp.Commitment, nil
}

// ComputeBlindingSum computes the sum of blinding factors.
func (c *Client) ComputeBlindingSum(ctx context.Context, positive, negative []string) (string, error) {
	body := map[string]interface{}{
		"positive": positive,
		"negative": negative,
	}
	var resp struct {
		Sum string `json:"sum"`
	}
	if err := c.request(ctx, "POST", "/commitments/blinding-sum", body, &resp); err != nil {
		return "", err
	}
	return resp.Sum, nil
}

// GenerateBlinding generates a random blinding factor.
func (c *Client) GenerateBlinding(ctx context.Context) (string, error) {
	var resp struct {
		Blinding string `json:"blinding"`
	}
	if err := c.request(ctx, "POST", "/commitments/random-blinding", nil, &resp); err != nil {
		return "", err
	}
	return resp.Blinding, nil
}

// ==================== Range Proofs ====================

// CreateRangeProofOptions are options for creating a range proof.
type CreateRangeProofOptions struct {
	Value     string
	Blinding  string
	Message   string
	BitLength int
}

// CreateRangeProof creates a Bulletproof range proof.
func (c *Client) CreateRangeProof(ctx context.Context, opts CreateRangeProofOptions) (*RangeProof, error) {
	body := map[string]interface{}{
		"value":    opts.Value,
		"blinding": opts.Blinding,
	}
	if opts.Message != "" {
		body["message"] = opts.Message
	}
	if opts.BitLength > 0 {
		body["bitLength"] = opts.BitLength
	} else {
		body["bitLength"] = 64
	}

	var resp struct {
		Proof RangeProof `json:"proof"`
	}
	if err := c.request(ctx, "POST", "/range-proofs/create", body, &resp); err != nil {
		return nil, err
	}
	return &resp.Proof, nil
}

// VerifyRangeProof verifies a Bulletproof range proof.
func (c *Client) VerifyRangeProof(ctx context.Context, commitment, proof string) (*VerifyRangeProofResult, error) {
	body := map[string]interface{}{
		"commitment": commitment,
		"proof":      proof,
	}
	var result VerifyRangeProofResult
	if err := c.request(ctx, "POST", "/range-proofs/verify", body, &result); err != nil {
		return nil, err
	}
	return &result, nil
}

// CreateAggregatedRangeProof creates an aggregated range proof.
func (c *Client) CreateAggregatedRangeProof(ctx context.Context, outputs []map[string]string) (string, error) {
	body := map[string]interface{}{
		"outputs": outputs,
	}
	var resp struct {
		Proof string `json:"proof"`
	}
	if err := c.request(ctx, "POST", "/range-proofs/aggregate", body, &resp); err != nil {
		return "", err
	}
	return resp.Proof, nil
}

// ==================== Lifecycle ====================

// HealthCheck checks if the service is healthy.
func (c *Client) HealthCheck(ctx context.Context) bool {
	var resp struct {
		Status string `json:"status"`
	}
	if err := c.request(ctx, "GET", "/health", nil, &resp); err != nil {
		return false
	}
	return resp.Status == "healthy"
}

// Close closes the client.
func (c *Client) Close() {
	c.closed.Store(true)
}

// IsClosed returns whether the client is closed.
func (c *Client) IsClosed() bool {
	return c.closed.Load()
}

// ==================== Private ====================

func (c *Client) request(ctx context.Context, method, path string, body interface{}, result interface{}) error {
	if c.closed.Load() {
		return &Error{Message: "client has been closed"}
	}

	var lastErr error
	for attempt := 0; attempt < c.config.Retries; attempt++ {
		err := c.doRequest(ctx, method, path, body, result)
		if err == nil {
			return nil
		}
		lastErr = err
		if attempt < c.config.Retries-1 {
			time.Sleep(time.Duration(1<<attempt) * time.Second)
		}
	}
	return lastErr
}

func (c *Client) doRequest(ctx context.Context, method, path string, body interface{}, result interface{}) error {
	url := c.config.Endpoint + path

	var bodyReader io.Reader
	if body != nil {
		jsonBody, err := json.Marshal(body)
		if err != nil {
			return &Error{Message: fmt.Sprintf("failed to marshal body: %v", err)}
		}
		bodyReader = bytes.NewReader(jsonBody)
	}

	req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
	if err != nil {
		return &Error{Message: fmt.Sprintf("failed to create request: %v", err)}
	}

	req.Header.Set("Authorization", "Bearer "+c.config.APIKey)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-SDK-Version", "go/0.1.0")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return &Error{Message: fmt.Sprintf("request failed: %v", err)}
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return &Error{Message: fmt.Sprintf("failed to read response: %v", err)}
	}

	if resp.StatusCode >= 400 {
		var errResp struct {
			Message string `json:"message"`
			Error   string `json:"error"`
			Code    string `json:"code"`
		}
		json.Unmarshal(respBody, &errResp)
		msg := errResp.Message
		if msg == "" {
			msg = errResp.Error
		}
		if msg == "" {
			msg = fmt.Sprintf("HTTP %d", resp.StatusCode)
		}
		return &Error{
			Message:    msg,
			Code:       errResp.Code,
			StatusCode: resp.StatusCode,
		}
	}

	if result != nil {
		if err := json.Unmarshal(respBody, result); err != nil {
			return &Error{Message: fmt.Sprintf("failed to parse response: %v", err)}
		}
	}

	return nil
}
