package io.synor.crypto;

import java.util.*;

/**
 * Synor Crypto SDK Types for Java
 */

// ============================================================================
// Enumerations
// ============================================================================

enum Network {
    MAINNET("mainnet"),
    TESTNET("testnet"),
    DEVNET("devnet");

    private final String value;
    Network(String value) { this.value = value; }
    public String getValue() { return value; }
    public static Network fromValue(String value) {
        for (Network n : values()) if (n.value.equals(value)) return n;
        return MAINNET;
    }
}

enum FalconVariant {
    FALCON512("falcon512", 690, 897, 128),
    FALCON1024("falcon1024", 1330, 1793, 256);

    private final String value;
    private final int signatureSize;
    private final int publicKeySize;
    private final int securityLevel;

    FalconVariant(String value, int sigSize, int pkSize, int secLevel) {
        this.value = value;
        this.signatureSize = sigSize;
        this.publicKeySize = pkSize;
        this.securityLevel = secLevel;
    }

    public String getValue() { return value; }
    public int getSignatureSize() { return signatureSize; }
    public int getPublicKeySize() { return publicKeySize; }
    public int getSecurityLevel() { return securityLevel; }
}

enum SphincsVariant {
    SHAKE128S("shake128s", 7856, 128),
    SHAKE192S("shake192s", 16224, 192),
    SHAKE256S("shake256s", 29792, 256);

    private final String value;
    private final int signatureSize;
    private final int securityLevel;

    SphincsVariant(String value, int sigSize, int secLevel) {
        this.value = value;
        this.signatureSize = sigSize;
        this.securityLevel = secLevel;
    }

    public String getValue() { return value; }
    public int getSignatureSize() { return signatureSize; }
    public int getSecurityLevel() { return securityLevel; }
}

enum PqAlgorithm {
    DILITHIUM3("dilithium3"),
    FALCON512("falcon512"),
    FALCON1024("falcon1024"),
    SPHINCS128S("sphincs128s"),
    SPHINCS192S("sphincs192s"),
    SPHINCS256S("sphincs256s");

    private final String value;
    PqAlgorithm(String value) { this.value = value; }
    public String getValue() { return value; }
}

enum AlgorithmFamily {
    CLASSICAL("classical"),
    LATTICE("lattice"),
    HASH_BASED("hash_based"),
    HYBRID("hybrid");

    private final String value;
    AlgorithmFamily(String value) { this.value = value; }
    public String getValue() { return value; }
}

// ============================================================================
// Configuration Types
// ============================================================================

class CryptoConfig {
    private final String apiKey;
    private String endpoint = "https://crypto.synor.io/v1";
    private int timeout = 30000;
    private int retries = 3;
    private boolean debug = false;
    private Network defaultNetwork = Network.MAINNET;

    public CryptoConfig(String apiKey) { this.apiKey = apiKey; }

    public String getApiKey() { return apiKey; }
    public String getEndpoint() { return endpoint; }
    public int getTimeout() { return timeout; }
    public int getRetries() { return retries; }
    public boolean isDebug() { return debug; }
    public Network getDefaultNetwork() { return defaultNetwork; }

    public CryptoConfig setEndpoint(String endpoint) { this.endpoint = endpoint; return this; }
    public CryptoConfig setTimeout(int timeout) { this.timeout = timeout; return this; }
    public CryptoConfig setRetries(int retries) { this.retries = retries; return this; }
    public CryptoConfig setDebug(boolean debug) { this.debug = debug; return this; }
    public CryptoConfig setDefaultNetwork(Network network) { this.defaultNetwork = network; return this; }
}

class DerivationConfig {
    private byte[] salt;
    private byte[] info;
    private int outputLength = 32;

    public byte[] getSalt() { return salt; }
    public byte[] getInfo() { return info; }
    public int getOutputLength() { return outputLength; }

    public DerivationConfig setSalt(byte[] salt) { this.salt = salt; return this; }
    public DerivationConfig setInfo(byte[] info) { this.info = info; return this; }
    public DerivationConfig setOutputLength(int len) { this.outputLength = len; return this; }
}

class PasswordDerivationConfig {
    private final byte[] salt;
    private int iterations = 100000;
    private int outputLength = 32;

    public PasswordDerivationConfig(byte[] salt) { this.salt = salt; }

    public byte[] getSalt() { return salt; }
    public int getIterations() { return iterations; }
    public int getOutputLength() { return outputLength; }

    public PasswordDerivationConfig setIterations(int iterations) { this.iterations = iterations; return this; }
    public PasswordDerivationConfig setOutputLength(int len) { this.outputLength = len; return this; }
}

class DerivationPath {
    public static final int COIN_TYPE = 0x5359;
    private final int account;
    private final int change;
    private final int index;

    public DerivationPath(int account, int change, int index) {
        this.account = account;
        this.change = change;
        this.index = index;
    }

    public static DerivationPath external(int account, int index) { return new DerivationPath(account, 0, index); }
    public static DerivationPath internal(int account, int index) { return new DerivationPath(account, 1, index); }

    public int getAccount() { return account; }
    public int getChange() { return change; }
    public int getIndex() { return index; }

    @Override
    public String toString() { return String.format("m/44'/%d'/%d'/%d/%d", COIN_TYPE, account, change, index); }

    public Map<String, Integer> toMap() { return Map.of("account", account, "change", change, "index", index); }
}

// ============================================================================
// Key Types
// ============================================================================

class HybridPublicKey {
    private String ed25519;
    private String dilithium;

    public byte[] getEd25519() { return Base64.getDecoder().decode(ed25519); }
    public byte[] getDilithium() { return Base64.getDecoder().decode(dilithium); }
    public int size() { return getEd25519().length + getDilithium().length; }

    public Map<String, String> toMap() {
        return Map.of("ed25519", ed25519, "dilithium", dilithium);
    }
}

class SecretKey {
    private String ed25519Seed;
    private String masterSeed;

    public byte[] getEd25519Seed() { return Base64.getDecoder().decode(ed25519Seed); }
    public byte[] getMasterSeed() { return Base64.getDecoder().decode(masterSeed); }

    public Map<String, String> toMap() {
        return Map.of("ed25519_seed", ed25519Seed, "master_seed", masterSeed);
    }
}

class FalconPublicKey {
    private String variant;
    private String bytes;

    public FalconVariant getVariant() { return FalconVariant.valueOf(variant.toUpperCase()); }
    public byte[] getBytes() { return Base64.getDecoder().decode(bytes); }
}

class FalconSecretKey {
    private String variant;
    private String bytes;

    public FalconVariant getVariant() { return FalconVariant.valueOf(variant.toUpperCase()); }
    public byte[] getBytes() { return Base64.getDecoder().decode(bytes); }
}

class SphincsPublicKey {
    private String variant;
    private String bytes;

    public SphincsVariant getVariant() { return SphincsVariant.valueOf(variant.toUpperCase()); }
    public byte[] getBytes() { return Base64.getDecoder().decode(bytes); }
}

class SphincsSecretKey {
    private String variant;
    private String bytes;

    public SphincsVariant getVariant() { return SphincsVariant.valueOf(variant.toUpperCase()); }
    public byte[] getBytes() { return Base64.getDecoder().decode(bytes); }
}

// ============================================================================
// Signature Types
// ============================================================================

class HybridSignature {
    private String ed25519;
    private String dilithium;

    public byte[] getEd25519() { return Base64.getDecoder().decode(ed25519); }
    public byte[] getDilithium() { return Base64.getDecoder().decode(dilithium); }
    public int size() { return getEd25519().length + getDilithium().length; }

    public byte[] toBytes() {
        byte[] e = getEd25519();
        byte[] d = getDilithium();
        byte[] result = new byte[e.length + d.length];
        System.arraycopy(e, 0, result, 0, e.length);
        System.arraycopy(d, 0, result, e.length, d.length);
        return result;
    }

    public Map<String, String> toMap() {
        return Map.of("ed25519", ed25519, "dilithium", dilithium);
    }
}

class FalconSignature {
    private String variant;
    private String signature;

    public FalconVariant getVariant() { return FalconVariant.valueOf(variant.toUpperCase()); }
    public byte[] getBytes() { return Base64.getDecoder().decode(signature); }
    public int size() { return getBytes().length; }
}

class SphincsSignature {
    private String variant;
    private String signature;

    public SphincsVariant getVariant() { return SphincsVariant.valueOf(variant.toUpperCase()); }
    public byte[] getBytes() { return Base64.getDecoder().decode(signature); }
    public int size() { return getBytes().length; }
}

// ============================================================================
// Keypair Types
// ============================================================================

class HybridKeypair {
    private HybridPublicKey publicKey;
    private SecretKey secretKey;
    private Map<String, String> addresses = new HashMap<>();

    public HybridPublicKey getPublicKey() { return publicKey; }
    public SecretKey getSecretKey() { return secretKey; }

    public String getAddress(Network network) {
        return addresses.getOrDefault(network.getValue(), "");
    }
}

class FalconKeypair {
    private String variant;
    private FalconPublicKey publicKey;
    private FalconSecretKey secretKey;

    public FalconVariant getVariant() { return FalconVariant.valueOf(variant.toUpperCase()); }
    public FalconPublicKey getPublicKey() { return publicKey; }
    public FalconSecretKey getSecretKey() { return secretKey; }
}

class SphincsKeypair {
    private String variant;
    private SphincsPublicKey publicKey;
    private SphincsSecretKey secretKey;

    public SphincsVariant getVariant() { return SphincsVariant.valueOf(variant.toUpperCase()); }
    public SphincsPublicKey getPublicKey() { return publicKey; }
    public SphincsSecretKey getSecretKey() { return secretKey; }
}

// ============================================================================
// Mnemonic Types
// ============================================================================

class Mnemonic {
    private String phrase;
    private List<String> words;
    private int wordCount;
    private String entropy;

    public String getPhrase() { return phrase; }
    public List<String> getWords() { return words != null ? words : Arrays.asList(phrase.split(" ")); }
    public int getWordCount() { return wordCount > 0 ? wordCount : getWords().size(); }
    public byte[] getEntropy() { return entropy != null ? Base64.getDecoder().decode(entropy) : new byte[0]; }
}

class MnemonicValidation {
    private boolean valid;
    private String error;

    public boolean isValid() { return valid; }
    public String getError() { return error; }
}

// ============================================================================
// Address Types
// ============================================================================

class Address {
    private String address;
    private String network;
    private String pubkeyHash;

    public String getAddress() { return address; }
    public Network getNetwork() { return Network.fromValue(network); }
    public byte[] getPubkeyHash() { return pubkeyHash != null ? Base64.getDecoder().decode(pubkeyHash) : new byte[0]; }
}

// ============================================================================
// Hash Types
// ============================================================================

class Hash256 {
    private String hash;

    public String getHex() { return hash; }
    public byte[] getBytes() {
        byte[] result = new byte[hash.length() / 2];
        for (int i = 0; i < result.length; i++) {
            result[i] = (byte) Integer.parseInt(hash.substring(i * 2, i * 2 + 2), 16);
        }
        return result;
    }
}

// ============================================================================
// Error Types
// ============================================================================

class CryptoException extends RuntimeException {
    private final String code;

    public CryptoException(String message, String code) {
        super(message);
        this.code = code;
    }

    public String getCode() { return code; }
}

// ============================================================================
// Constants
// ============================================================================

final class CryptoConstants {
    public static final int ED25519_PUBLIC_KEY_SIZE = 32;
    public static final int ED25519_SECRET_KEY_SIZE = 32;
    public static final int ED25519_SIGNATURE_SIZE = 64;
    public static final int DILITHIUM3_PUBLIC_KEY_SIZE = 1952;
    public static final int DILITHIUM3_SIGNATURE_SIZE = 3293;
    public static final int HYBRID_SIGNATURE_SIZE = 64 + 3293;
    public static final int COIN_TYPE = 0x5359;
    public static final int MIN_PBKDF2_ITERATIONS = 10000;
    public static final int MIN_SALT_LENGTH = 8;
    public static final String DEFAULT_ENDPOINT = "https://crypto.synor.io/v1";

    private CryptoConstants() {}
}
