// Package crypto provides quantum-resistant cryptographic primitives for the Synor blockchain.
package crypto

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

// SynorCrypto is the main client for the Crypto SDK
type SynorCrypto struct {
	config Config
	client *http.Client
	closed bool

	Mnemonic    *MnemonicClient
	Keypairs    *KeypairClient
	Signing     *SigningClient
	Falcon      *FalconClient
	Sphincs     *SphincsClient
	KDF         *KDFClient
	Hash        *HashClient
	Negotiation *NegotiationClient
}

// New creates a new SynorCrypto client
func New(config Config) *SynorCrypto {
	if config.Endpoint == "" {
		config.Endpoint = DefaultEndpoint
	}
	if config.Timeout == 0 {
		config.Timeout = 30000
	}
	if config.Retries == 0 {
		config.Retries = 3
	}
	if config.DefaultNetwork == "" {
		config.DefaultNetwork = NetworkMainnet
	}

	c := &SynorCrypto{
		config: config,
		client: &http.Client{
			Timeout: time.Duration(config.Timeout) * time.Millisecond,
		},
	}

	c.Mnemonic = &MnemonicClient{crypto: c}
	c.Keypairs = &KeypairClient{crypto: c}
	c.Signing = &SigningClient{crypto: c}
	c.Falcon = &FalconClient{crypto: c}
	c.Sphincs = &SphincsClient{crypto: c}
	c.KDF = &KDFClient{crypto: c}
	c.Hash = &HashClient{crypto: c}
	c.Negotiation = &NegotiationClient{crypto: c}

	return c
}

// DefaultNetwork returns the default network
func (c *SynorCrypto) DefaultNetwork() Network {
	return c.config.DefaultNetwork
}

// HealthCheck checks if the service is healthy
func (c *SynorCrypto) HealthCheck(ctx context.Context) (bool, error) {
	var result map[string]interface{}
	if err := c.get(ctx, "/health", &result); err != nil {
		return false, nil
	}
	status, _ := result["status"].(string)
	return status == "healthy", nil
}

// GetInfo returns service information
func (c *SynorCrypto) GetInfo(ctx context.Context) (map[string]interface{}, error) {
	var result map[string]interface{}
	err := c.get(ctx, "/info", &result)
	return result, err
}

// Close closes the client
func (c *SynorCrypto) Close() {
	c.closed = true
}

// IsClosed returns true if the client is closed
func (c *SynorCrypto) IsClosed() bool {
	return c.closed
}

func (c *SynorCrypto) get(ctx context.Context, path string, result interface{}) error {
	if c.closed {
		return NewCryptoError("Client has been closed", "CLIENT_CLOSED")
	}

	req, err := http.NewRequestWithContext(ctx, "GET", c.config.Endpoint+path, nil)
	if err != nil {
		return err
	}
	c.setHeaders(req)

	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return c.handleResponse(resp, result)
}

func (c *SynorCrypto) post(ctx context.Context, path string, body interface{}, result interface{}) error {
	if c.closed {
		return NewCryptoError("Client has been closed", "CLIENT_CLOSED")
	}

	var reqBody io.Reader
	if body != nil {
		jsonBody, err := json.Marshal(body)
		if err != nil {
			return err
		}
		reqBody = bytes.NewBuffer(jsonBody)
	}

	req, err := http.NewRequestWithContext(ctx, "POST", c.config.Endpoint+path, reqBody)
	if err != nil {
		return err
	}
	c.setHeaders(req)

	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	return c.handleResponse(resp, result)
}

func (c *SynorCrypto) setHeaders(req *http.Request) {
	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")
}

func (c *SynorCrypto) handleResponse(resp *http.Response, result interface{}) error {
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if resp.StatusCode >= 400 {
		var errResp map[string]interface{}
		json.Unmarshal(body, &errResp)
		msg := fmt.Sprintf("HTTP %d", resp.StatusCode)
		code := ""
		if m, ok := errResp["message"].(string); ok {
			msg = m
		}
		if c, ok := errResp["code"].(string); ok {
			code = c
		}
		return NewCryptoError(msg, code)
	}

	return json.Unmarshal(body, result)
}

// ============================================================================
// Mnemonic Client
// ============================================================================

// MnemonicClient handles mnemonic operations
type MnemonicClient struct {
	crypto *SynorCrypto
}

// Generate creates a new random mnemonic
func (c *MnemonicClient) Generate(ctx context.Context, wordCount int) (*Mnemonic, error) {
	var result mnemonicResponse
	err := c.crypto.post(ctx, "/mnemonic/generate", map[string]int{"word_count": wordCount}, &result)
	if err != nil {
		return nil, err
	}
	return result.toMnemonic()
}

// FromPhrase creates a mnemonic from a phrase
func (c *MnemonicClient) FromPhrase(ctx context.Context, phrase string) (*Mnemonic, error) {
	var result mnemonicResponse
	err := c.crypto.post(ctx, "/mnemonic/from-phrase", map[string]string{"phrase": phrase}, &result)
	if err != nil {
		return nil, err
	}
	return result.toMnemonic()
}

// FromEntropy creates a mnemonic from entropy
func (c *MnemonicClient) FromEntropy(ctx context.Context, entropy []byte) (*Mnemonic, error) {
	var result mnemonicResponse
	err := c.crypto.post(ctx, "/mnemonic/from-entropy", map[string]string{
		"entropy": EncodeBase64(entropy),
	}, &result)
	if err != nil {
		return nil, err
	}
	return result.toMnemonic()
}

// Validate checks if a phrase is valid
func (c *MnemonicClient) Validate(ctx context.Context, phrase string) (*MnemonicValidation, error) {
	var result MnemonicValidation
	err := c.crypto.post(ctx, "/mnemonic/validate", map[string]string{"phrase": phrase}, &result)
	return &result, err
}

// ToSeed derives a seed from a mnemonic
func (c *MnemonicClient) ToSeed(ctx context.Context, phrase string, passphrase string) ([]byte, error) {
	var result struct {
		Seed string `json:"seed"`
	}
	err := c.crypto.post(ctx, "/mnemonic/to-seed", map[string]string{
		"phrase":     phrase,
		"passphrase": passphrase,
	}, &result)
	if err != nil {
		return nil, err
	}
	return DecodeBase64(result.Seed)
}

// SuggestWords suggests completions for a partial word
func (c *MnemonicClient) SuggestWords(ctx context.Context, partial string, limit int) ([]string, error) {
	var result struct {
		Suggestions []string `json:"suggestions"`
	}
	err := c.crypto.post(ctx, "/mnemonic/suggest", map[string]interface{}{
		"partial": partial,
		"limit":   limit,
	}, &result)
	return result.Suggestions, err
}

type mnemonicResponse struct {
	Phrase    string   `json:"phrase"`
	Words     []string `json:"words"`
	WordCount int      `json:"word_count"`
	Entropy   string   `json:"entropy"`
}

func (r *mnemonicResponse) toMnemonic() (*Mnemonic, error) {
	entropy, _ := DecodeBase64(r.Entropy)
	return &Mnemonic{
		Phrase:    r.Phrase,
		Words:     r.Words,
		WordCount: r.WordCount,
		Entropy:   entropy,
	}, nil
}

// ============================================================================
// Keypair Client
// ============================================================================

// KeypairClient handles keypair operations
type KeypairClient struct {
	crypto *SynorCrypto
}

// Generate creates a new random keypair
func (c *KeypairClient) Generate(ctx context.Context) (*HybridKeypair, error) {
	var result keypairResponse
	err := c.crypto.post(ctx, "/keypair/generate", nil, &result)
	if err != nil {
		return nil, err
	}
	return result.toKeypair()
}

// FromMnemonic creates a keypair from a mnemonic
func (c *KeypairClient) FromMnemonic(ctx context.Context, phrase string, passphrase string) (*HybridKeypair, error) {
	var result keypairResponse
	err := c.crypto.post(ctx, "/keypair/from-mnemonic", map[string]string{
		"phrase":     phrase,
		"passphrase": passphrase,
	}, &result)
	if err != nil {
		return nil, err
	}
	return result.toKeypair()
}

// FromSeed creates a keypair from a seed
func (c *KeypairClient) FromSeed(ctx context.Context, seed []byte) (*HybridKeypair, error) {
	var result keypairResponse
	err := c.crypto.post(ctx, "/keypair/from-seed", map[string]string{
		"seed": EncodeBase64(seed),
	}, &result)
	if err != nil {
		return nil, err
	}
	return result.toKeypair()
}

// GetAddress gets the address for a public key
func (c *KeypairClient) GetAddress(ctx context.Context, pk *HybridPublicKey, network Network) (*Address, error) {
	var result Address
	err := c.crypto.post(ctx, "/keypair/address", map[string]interface{}{
		"public_key": map[string]string{
			"ed25519":   EncodeBase64(pk.Ed25519),
			"dilithium": EncodeBase64(pk.Dilithium),
		},
		"network": network,
	}, &result)
	return &result, err
}

type keypairResponse struct {
	PublicKey struct {
		Ed25519   string `json:"ed25519"`
		Dilithium string `json:"dilithium"`
	} `json:"public_key"`
	SecretKey struct {
		Ed25519Seed string `json:"ed25519_seed"`
		MasterSeed  string `json:"master_seed"`
	} `json:"secret_key"`
	Addresses map[string]string `json:"addresses"`
}

func (r *keypairResponse) toKeypair() (*HybridKeypair, error) {
	ed25519, _ := DecodeBase64(r.PublicKey.Ed25519)
	dilithium, _ := DecodeBase64(r.PublicKey.Dilithium)
	ed25519Seed, _ := DecodeBase64(r.SecretKey.Ed25519Seed)
	masterSeed, _ := DecodeBase64(r.SecretKey.MasterSeed)

	addresses := make(map[Network]string)
	for k, v := range r.Addresses {
		addresses[Network(k)] = v
	}

	return &HybridKeypair{
		PublicKey: &HybridPublicKey{
			Ed25519:   ed25519,
			Dilithium: dilithium,
		},
		SecretKey: &SecretKey{
			Ed25519Seed: ed25519Seed,
			MasterSeed:  masterSeed,
		},
		addresses: addresses,
	}, nil
}

// ============================================================================
// Signing Client
// ============================================================================

// SigningClient handles signing operations
type SigningClient struct {
	crypto *SynorCrypto
}

// Sign signs a message with a hybrid keypair
func (c *SigningClient) Sign(ctx context.Context, kp *HybridKeypair, message []byte) (*HybridSignature, error) {
	var result struct {
		Ed25519   string `json:"ed25519"`
		Dilithium string `json:"dilithium"`
	}
	err := c.crypto.post(ctx, "/sign/hybrid", map[string]interface{}{
		"secret_key": map[string]string{
			"ed25519_seed": EncodeBase64(kp.SecretKey.Ed25519Seed),
			"master_seed":  EncodeBase64(kp.SecretKey.MasterSeed),
		},
		"message": EncodeBase64(message),
	}, &result)
	if err != nil {
		return nil, err
	}

	ed25519, _ := DecodeBase64(result.Ed25519)
	dilithium, _ := DecodeBase64(result.Dilithium)
	return &HybridSignature{
		Ed25519:   ed25519,
		Dilithium: dilithium,
	}, nil
}

// Verify verifies a hybrid signature
func (c *SigningClient) Verify(ctx context.Context, pk *HybridPublicKey, message []byte, sig *HybridSignature) (bool, error) {
	var result struct {
		Valid bool `json:"valid"`
	}
	err := c.crypto.post(ctx, "/sign/verify", map[string]interface{}{
		"public_key": map[string]string{
			"ed25519":   EncodeBase64(pk.Ed25519),
			"dilithium": EncodeBase64(pk.Dilithium),
		},
		"message": EncodeBase64(message),
		"signature": map[string]string{
			"ed25519":   EncodeBase64(sig.Ed25519),
			"dilithium": EncodeBase64(sig.Dilithium),
		},
	}, &result)
	return result.Valid, err
}

// SignEd25519 signs with Ed25519 only
func (c *SigningClient) SignEd25519(ctx context.Context, secretKey []byte, message []byte) ([]byte, error) {
	var result struct {
		Signature string `json:"signature"`
	}
	err := c.crypto.post(ctx, "/sign/ed25519", map[string]string{
		"secret_key": EncodeBase64(secretKey),
		"message":    EncodeBase64(message),
	}, &result)
	if err != nil {
		return nil, err
	}
	return DecodeBase64(result.Signature)
}

// ============================================================================
// Falcon Client
// ============================================================================

// FalconClient handles Falcon operations
type FalconClient struct {
	crypto *SynorCrypto
}

// Generate creates a Falcon keypair
func (c *FalconClient) Generate(ctx context.Context, variant FalconVariant) (*FalconKeypair, error) {
	var result map[string]interface{}
	err := c.crypto.post(ctx, "/falcon/generate", map[string]string{"variant": string(variant)}, &result)
	if err != nil {
		return nil, err
	}
	// Parse response into FalconKeypair
	return parseFalconKeypair(result, variant)
}

// Sign signs with Falcon
func (c *FalconClient) Sign(ctx context.Context, kp *FalconKeypair, message []byte) (*FalconSignature, error) {
	var result struct {
		Signature string `json:"signature"`
		Variant   string `json:"variant"`
	}
	err := c.crypto.post(ctx, "/falcon/sign", map[string]string{
		"variant":    string(kp.Variant),
		"secret_key": EncodeBase64(kp.SecretKey.Bytes),
		"message":    EncodeBase64(message),
	}, &result)
	if err != nil {
		return nil, err
	}
	sigBytes, _ := DecodeBase64(result.Signature)
	return &FalconSignature{
		Variant: FalconVariant(result.Variant),
		Bytes:   sigBytes,
	}, nil
}

// Verify verifies a Falcon signature
func (c *FalconClient) Verify(ctx context.Context, publicKey []byte, message []byte, sig *FalconSignature) (bool, error) {
	var result struct {
		Valid bool `json:"valid"`
	}
	err := c.crypto.post(ctx, "/falcon/verify", map[string]interface{}{
		"variant":    sig.Variant,
		"public_key": EncodeBase64(publicKey),
		"message":    EncodeBase64(message),
		"signature":  EncodeBase64(sig.Bytes),
	}, &result)
	return result.Valid, err
}

func parseFalconKeypair(data map[string]interface{}, variant FalconVariant) (*FalconKeypair, error) {
	pk := data["public_key"].(map[string]interface{})
	sk := data["secret_key"].(map[string]interface{})
	pkBytes, _ := DecodeBase64(pk["bytes"].(string))
	skBytes, _ := DecodeBase64(sk["bytes"].(string))
	return &FalconKeypair{
		Variant:   variant,
		PublicKey: &FalconPublicKey{Variant: variant, Bytes: pkBytes},
		SecretKey: &FalconSecretKey{Variant: variant, Bytes: skBytes},
	}, nil
}

// ============================================================================
// SPHINCS+ Client
// ============================================================================

// SphincsClient handles SPHINCS+ operations
type SphincsClient struct {
	crypto *SynorCrypto
}

// Generate creates a SPHINCS+ keypair
func (c *SphincsClient) Generate(ctx context.Context, variant SphincsVariant) (*SphincsKeypair, error) {
	var result map[string]interface{}
	err := c.crypto.post(ctx, "/sphincs/generate", map[string]string{"variant": string(variant)}, &result)
	if err != nil {
		return nil, err
	}
	return parseSphincsKeypair(result, variant)
}

// Sign signs with SPHINCS+
func (c *SphincsClient) Sign(ctx context.Context, kp *SphincsKeypair, message []byte) (*SphincsSignature, error) {
	var result struct {
		Signature string `json:"signature"`
		Variant   string `json:"variant"`
	}
	err := c.crypto.post(ctx, "/sphincs/sign", map[string]string{
		"variant":    string(kp.Variant),
		"secret_key": EncodeBase64(kp.SecretKey.Bytes),
		"message":    EncodeBase64(message),
	}, &result)
	if err != nil {
		return nil, err
	}
	sigBytes, _ := DecodeBase64(result.Signature)
	return &SphincsSignature{
		Variant: SphincsVariant(result.Variant),
		Bytes:   sigBytes,
	}, nil
}

// Verify verifies a SPHINCS+ signature
func (c *SphincsClient) Verify(ctx context.Context, publicKey []byte, message []byte, sig *SphincsSignature) (bool, error) {
	var result struct {
		Valid bool `json:"valid"`
	}
	err := c.crypto.post(ctx, "/sphincs/verify", map[string]interface{}{
		"variant":    sig.Variant,
		"public_key": EncodeBase64(publicKey),
		"message":    EncodeBase64(message),
		"signature":  EncodeBase64(sig.Bytes),
	}, &result)
	return result.Valid, err
}

func parseSphincsKeypair(data map[string]interface{}, variant SphincsVariant) (*SphincsKeypair, error) {
	pk := data["public_key"].(map[string]interface{})
	sk := data["secret_key"].(map[string]interface{})
	pkBytes, _ := DecodeBase64(pk["bytes"].(string))
	skBytes, _ := DecodeBase64(sk["bytes"].(string))
	return &SphincsKeypair{
		Variant:   variant,
		PublicKey: &SphincsPublicKey{Variant: variant, Bytes: pkBytes},
		SecretKey: &SphincsSecretKey{Variant: variant, Bytes: skBytes},
	}, nil
}

// ============================================================================
// KDF Client
// ============================================================================

// KDFClient handles key derivation
type KDFClient struct {
	crypto *SynorCrypto
}

// DeriveKey derives a key using HKDF
func (c *KDFClient) DeriveKey(ctx context.Context, seed []byte, config *DerivationConfig) ([]byte, error) {
	body := map[string]interface{}{
		"seed":          EncodeBase64(seed),
		"output_length": config.OutputLength,
	}
	if config.Salt != nil {
		body["salt"] = EncodeBase64(config.Salt)
	}
	if config.Info != nil {
		body["info"] = EncodeBase64(config.Info)
	}

	var result struct {
		Key string `json:"key"`
	}
	err := c.crypto.post(ctx, "/kdf/hkdf", body, &result)
	if err != nil {
		return nil, err
	}
	return DecodeBase64(result.Key)
}

// DeriveFromPassword derives a key from a password
func (c *KDFClient) DeriveFromPassword(ctx context.Context, password []byte, config *PasswordDerivationConfig) ([]byte, error) {
	var result struct {
		Key string `json:"key"`
	}
	err := c.crypto.post(ctx, "/kdf/pbkdf2", map[string]interface{}{
		"password":      EncodeBase64(password),
		"salt":          EncodeBase64(config.Salt),
		"iterations":    config.Iterations,
		"output_length": config.OutputLength,
	}, &result)
	if err != nil {
		return nil, err
	}
	return DecodeBase64(result.Key)
}

// DeriveChildKey derives a child key
func (c *KDFClient) DeriveChildKey(ctx context.Context, parentKey, chainCode []byte, index int) ([]byte, []byte, error) {
	var result struct {
		Key       string `json:"key"`
		ChainCode string `json:"chain_code"`
	}
	err := c.crypto.post(ctx, "/kdf/child", map[string]interface{}{
		"parent_key": EncodeBase64(parentKey),
		"chain_code": EncodeBase64(chainCode),
		"index":      index,
	}, &result)
	if err != nil {
		return nil, nil, err
	}
	key, _ := DecodeBase64(result.Key)
	cc, _ := DecodeBase64(result.ChainCode)
	return key, cc, nil
}

// ============================================================================
// Hash Client
// ============================================================================

// HashClient handles hashing operations
type HashClient struct {
	crypto *SynorCrypto
}

// SHA3_256 computes SHA3-256 hash
func (c *HashClient) SHA3_256(ctx context.Context, data []byte) (*Hash256, error) {
	var result struct {
		Hash string `json:"hash"`
	}
	err := c.crypto.post(ctx, "/hash/sha3-256", map[string]string{
		"data": EncodeBase64(data),
	}, &result)
	if err != nil {
		return nil, err
	}
	hashBytes, _ := DecodeBase64(result.Hash)
	return &Hash256{Bytes: hashBytes, Hex: result.Hash}, nil
}

// Blake3 computes BLAKE3 hash
func (c *HashClient) Blake3(ctx context.Context, data []byte) (*Hash256, error) {
	var result struct {
		Hash string `json:"hash"`
	}
	err := c.crypto.post(ctx, "/hash/blake3", map[string]string{
		"data": EncodeBase64(data),
	}, &result)
	if err != nil {
		return nil, err
	}
	hashBytes, _ := DecodeBase64(result.Hash)
	return &Hash256{Bytes: hashBytes, Hex: result.Hash}, nil
}

// ============================================================================
// Negotiation Client
// ============================================================================

// NegotiationClient handles algorithm negotiation
type NegotiationClient struct {
	crypto *SynorCrypto
}

// GetCapabilities returns local capabilities
func (c *NegotiationClient) GetCapabilities(ctx context.Context) (*AlgorithmCapabilities, error) {
	var result AlgorithmCapabilities
	err := c.crypto.get(ctx, "/negotiation/capabilities", &result)
	return &result, err
}

// Negotiate negotiates with a peer
func (c *NegotiationClient) Negotiate(ctx context.Context, peerCaps *AlgorithmCapabilities, policy *NegotiationPolicy) (*NegotiationResult, error) {
	var result NegotiationResult
	err := c.crypto.post(ctx, "/negotiation/negotiate", map[string]interface{}{
		"peer_capabilities": peerCaps,
		"policy":            policy,
	}, &result)
	return &result, err
}
