package io.synor.examples;

import io.synor.crypto.*;
import io.synor.crypto.types.*;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;

/**
 * Synor Crypto SDK Examples for Java
 *
 * Demonstrates quantum-resistant cryptographic operations:
 * - Hybrid Ed25519 + Dilithium3 signatures
 * - BIP-39 mnemonic generation and validation
 * - Post-quantum algorithms (Falcon, SPHINCS+)
 * - Key derivation functions
 */
public class CryptoExample {

    public static void main(String[] args) throws Exception {
        // Initialize client
        CryptoConfig config = CryptoConfig.builder()
            .apiKey(System.getenv("SYNOR_API_KEY") != null ?
                    System.getenv("SYNOR_API_KEY") : "your-api-key")
            .endpoint("https://crypto.synor.io/v1")
            .timeout(30000)
            .retries(3)
            .debug(false)
            .defaultNetwork(Network.MAINNET)
            .build();

        SynorCrypto crypto = new SynorCrypto(config);

        try {
            // Check service health
            boolean healthy = crypto.healthCheck().get();
            System.out.println("Service healthy: " + healthy + "\n");

            // Run examples
            mnemonicExample(crypto);
            keypairExample(crypto);
            signingExample(crypto);
            falconExample(crypto);
            sphincsExample(crypto);
            kdfExample(crypto);
            hashExample(crypto);
        } finally {
            crypto.close();
        }
    }

    static void mnemonicExample(SynorCrypto crypto) throws Exception {
        System.out.println("=== Mnemonic Operations ===");

        // Generate a 24-word mnemonic (256-bit entropy)
        Mnemonic mnemonic = crypto.mnemonic().generate(24).get();
        System.out.println("Generated mnemonic: " + mnemonic.getPhrase());
        System.out.println("Word count: " + mnemonic.getWordCount());

        // Validate a mnemonic
        ValidationResult validation = crypto.mnemonic().validate(mnemonic.getPhrase()).get();
        System.out.println("Valid: " + validation.isValid());
        if (!validation.isValid()) {
            System.out.println("Error: " + validation.getError());
        }

        // Convert mnemonic to seed
        byte[] seed = crypto.mnemonic().toSeed(mnemonic.getPhrase(), "optional-passphrase").get();
        System.out.println("Seed (hex): " + bytesToHex(seed).substring(0, 32) + "...");

        // Word suggestions for autocomplete
        List<String> suggestions = crypto.mnemonic().suggestWords("aban", 5).get();
        System.out.println("Suggestions for 'aban': " + String.join(", ", suggestions));

        System.out.println();
    }

    static void keypairExample(SynorCrypto crypto) throws Exception {
        System.out.println("=== Keypair Operations ===");

        // Generate a random keypair
        HybridKeypair keypair = crypto.keypairs().generate().get();
        System.out.println("Generated hybrid keypair:");
        System.out.println("  Ed25519 public key size: " + keypair.getPublicKey().getEd25519Bytes().length + " bytes");
        System.out.println("  Dilithium public key size: " + keypair.getPublicKey().getDilithiumBytes().length + " bytes");
        System.out.println("  Total public key size: " + keypair.getPublicKey().getSize() + " bytes");

        // Get addresses for different networks
        System.out.println("\nAddresses:");
        System.out.println("  Mainnet: " + keypair.getAddress(Network.MAINNET));
        System.out.println("  Testnet: " + keypair.getAddress(Network.TESTNET));
        System.out.println("  Devnet: " + keypair.getAddress(Network.DEVNET));

        // Create keypair from mnemonic (deterministic)
        Mnemonic mnemonic = crypto.mnemonic().generate(24).get();
        HybridKeypair keypair2 = crypto.keypairs().fromMnemonic(mnemonic.getPhrase(), "").get();
        String addr = keypair2.getAddress(Network.MAINNET);
        System.out.println("\nKeypair from mnemonic: " + addr.substring(0, 20) + "...");

        // Derive child keypair using BIP-44 path
        DerivationPath path = DerivationPath.external(0, 0); // m/44'/21337'/0'/0/0
        System.out.println("Derivation path: " + path);

        System.out.println();
    }

    static void signingExample(SynorCrypto crypto) throws Exception {
        System.out.println("=== Hybrid Signing ===");

        // Generate keypair
        HybridKeypair keypair = crypto.keypairs().generate().get();

        // Sign a message
        byte[] message = "Hello, quantum-resistant world!".getBytes();
        HybridSignature signature = crypto.signing().sign(keypair, message).get();

        System.out.println("Signature created:");
        System.out.println("  Ed25519 component: " + signature.getEd25519Bytes().length + " bytes");
        System.out.println("  Dilithium component: " + signature.getDilithiumBytes().length + " bytes");
        System.out.println("  Total signature size: " + signature.getSize() + " bytes");

        // Verify the signature
        boolean valid = crypto.signing().verify(keypair.getPublicKey(), message, signature).get();
        System.out.println("\nVerification result: " + valid);

        // Verify with tampered message fails
        byte[] tamperedMessage = "Hello, tampered message!".getBytes();
        boolean invalidResult = crypto.signing().verify(keypair.getPublicKey(), tamperedMessage, signature).get();
        System.out.println("Tampered message verification: " + invalidResult);

        System.out.println();
    }

    static void falconExample(SynorCrypto crypto) throws Exception {
        System.out.println("=== Falcon Post-Quantum Signatures ===");

        // Generate Falcon-512 keypair (128-bit security)
        FalconKeypair falcon512 = crypto.falcon().generate(FalconVariant.FALCON512).get();
        System.out.println("Falcon-512 keypair:");
        System.out.println("  Public key: " + falcon512.getPublicKey().getKeyBytes().length + " bytes");
        System.out.println("  Security level: 128-bit");

        // Generate Falcon-1024 keypair (256-bit security)
        FalconKeypair falcon1024 = crypto.falcon().generate(FalconVariant.FALCON1024).get();
        System.out.println("\nFalcon-1024 keypair:");
        System.out.println("  Public key: " + falcon1024.getPublicKey().getKeyBytes().length + " bytes");
        System.out.println("  Security level: 256-bit");

        // Sign with Falcon-512
        byte[] message = "Post-quantum secure message".getBytes();
        FalconSignature signature = crypto.falcon().sign(falcon512, message).get();
        System.out.println("\nFalcon-512 signature: " + signature.getSignatureBytes().length + " bytes");

        // Verify
        boolean valid = crypto.falcon().verify(falcon512.getPublicKey().getKeyBytes(), message, signature).get();
        System.out.println("Verification: " + valid);

        System.out.println();
    }

    static void sphincsExample(SynorCrypto crypto) throws Exception {
        System.out.println("=== SPHINCS+ Hash-Based Signatures ===");

        // SPHINCS+ variants with different security levels
        Object[][] variants = {
            {SphincsVariant.SHAKE128S, 128, 7856},
            {SphincsVariant.SHAKE192S, 192, 16224},
            {SphincsVariant.SHAKE256S, 256, 29792},
        };

        // Generate and demonstrate each variant
        for (Object[] v : variants) {
            SphincsVariant variant = (SphincsVariant) v[0];
            int security = (int) v[1];
            int sigSize = (int) v[2];

            SphincsKeypair keypair = crypto.sphincs().generate(variant).get();
            System.out.println("SPHINCS+ " + variant + ":");
            System.out.println("  Security level: " + security + "-bit");
            System.out.println("  Expected signature size: " + sigSize + " bytes");

            // Sign a message
            byte[] message = "Hash-based quantum security".getBytes();
            SphincsSignature signature = crypto.sphincs().sign(keypair, message).get();
            System.out.println("  Actual signature size: " + signature.getSignatureBytes().length + " bytes");

            // Verify
            boolean valid = crypto.sphincs().verify(keypair.getPublicKey().getKeyBytes(), message, signature).get();
            System.out.println("  Verification: " + valid + "\n");
        }
    }

    static void kdfExample(SynorCrypto crypto) throws Exception {
        System.out.println("=== Key Derivation Functions ===");

        // HKDF (HMAC-based Key Derivation Function)
        byte[] seed = "master-secret-key-material-here".getBytes();
        DerivationConfig hkdfConfig = DerivationConfig.builder()
            .salt("application-salt".getBytes())
            .info("encryption-key".getBytes())
            .outputLength(32)
            .build();

        byte[] derivedKey = crypto.kdf().deriveKey(seed, hkdfConfig).get();
        System.out.println("HKDF derived key: " + bytesToHex(derivedKey));

        // PBKDF2 (Password-Based Key Derivation Function)
        byte[] password = "user-password".getBytes();
        PasswordDerivationConfig pbkdf2Config = PasswordDerivationConfig.builder()
            .salt("random-salt-value".getBytes())
            .iterations(100000)
            .outputLength(32)
            .build();

        byte[] passwordKey = crypto.kdf().deriveFromPassword(password, pbkdf2Config).get();
        System.out.println("PBKDF2 derived key: " + bytesToHex(passwordKey));

        System.out.println();
    }

    static void hashExample(SynorCrypto crypto) throws Exception {
        System.out.println("=== Hash Functions ===");

        byte[] data = "Data to hash".getBytes();

        // SHA3-256 (FIPS 202)
        HashResult sha3 = crypto.hash().sha3_256(data).get();
        System.out.println("SHA3-256: " + sha3.getHex());

        // BLAKE3 (fast, parallel)
        HashResult blake3 = crypto.hash().blake3(data).get();
        System.out.println("BLAKE3:   " + blake3.getHex());

        // Keccak-256 (Ethereum compatible)
        HashResult keccak = crypto.hash().keccak256(data).get();
        System.out.println("Keccak:   " + keccak.getHex());

        System.out.println();
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
}
