using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Synor.Crypto;

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

/// <summary>Network type for the Synor blockchain.</summary>
public enum Network
{
    Mainnet,
    Testnet,
    Devnet
}

public static class NetworkExtensions
{
    public static string ToApiString(this Network network) => network switch
    {
        Network.Mainnet => "mainnet",
        Network.Testnet => "testnet",
        Network.Devnet => "devnet",
        _ => "mainnet"
    };

    public static Network FromApiString(string value) => value switch
    {
        "mainnet" => Network.Mainnet,
        "testnet" => Network.Testnet,
        "devnet" => Network.Devnet,
        _ => Network.Mainnet
    };
}

/// <summary>Falcon signature variant.</summary>
public enum FalconVariant
{
    Falcon512,
    Falcon1024
}

public static class FalconVariantExtensions
{
    public static string ToApiString(this FalconVariant variant) => variant switch
    {
        FalconVariant.Falcon512 => "falcon512",
        FalconVariant.Falcon1024 => "falcon1024",
        _ => "falcon512"
    };

    public static int SignatureSize(this FalconVariant variant) => variant switch
    {
        FalconVariant.Falcon512 => 690,
        FalconVariant.Falcon1024 => 1330,
        _ => 690
    };

    public static int PublicKeySize(this FalconVariant variant) => variant switch
    {
        FalconVariant.Falcon512 => 897,
        FalconVariant.Falcon1024 => 1793,
        _ => 897
    };

    public static int SecurityLevel(this FalconVariant variant) => variant switch
    {
        FalconVariant.Falcon512 => 128,
        FalconVariant.Falcon1024 => 256,
        _ => 128
    };
}

/// <summary>SPHINCS+ signature variant.</summary>
public enum SphincsVariant
{
    Shake128s,
    Shake192s,
    Shake256s
}

public static class SphincsVariantExtensions
{
    public static string ToApiString(this SphincsVariant variant) => variant switch
    {
        SphincsVariant.Shake128s => "shake128s",
        SphincsVariant.Shake192s => "shake192s",
        SphincsVariant.Shake256s => "shake256s",
        _ => "shake128s"
    };

    public static int SignatureSize(this SphincsVariant variant) => variant switch
    {
        SphincsVariant.Shake128s => 7856,
        SphincsVariant.Shake192s => 16224,
        SphincsVariant.Shake256s => 29792,
        _ => 7856
    };

    public static int SecurityLevel(this SphincsVariant variant) => variant switch
    {
        SphincsVariant.Shake128s => 128,
        SphincsVariant.Shake192s => 192,
        SphincsVariant.Shake256s => 256,
        _ => 128
    };
}

/// <summary>Post-quantum algorithm types.</summary>
public enum PqAlgorithm
{
    Dilithium3,
    Falcon512,
    Falcon1024,
    Sphincs128s,
    Sphincs192s,
    Sphincs256s
}

/// <summary>Cryptographic algorithm family.</summary>
public enum AlgorithmFamily
{
    Classical,
    Lattice,
    HashBased,
    Hybrid
}

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

/// <summary>Configuration for the Crypto SDK.</summary>
public record CryptoConfig
{
    public required string ApiKey { get; init; }
    public string Endpoint { get; init; } = "https://crypto.synor.io/v1";
    public int Timeout { get; init; } = 30000;
    public int Retries { get; init; } = 3;
    public bool Debug { get; init; } = false;
    public Network DefaultNetwork { get; init; } = Network.Mainnet;
}

/// <summary>Configuration for key derivation.</summary>
public record DerivationConfig
{
    public byte[]? Salt { get; init; }
    public byte[]? Info { get; init; }
    public int OutputLength { get; init; } = 32;
}

/// <summary>Configuration for password-based key derivation.</summary>
public record PasswordDerivationConfig
{
    public required byte[] Salt { get; init; }
    public int Iterations { get; init; } = 100000;
    public int OutputLength { get; init; } = 32;
}

/// <summary>BIP-44 derivation path.</summary>
public record DerivationPath(int Account, int Change, int Index)
{
    public const int CoinType = 0x5359;

    public static DerivationPath External(int account, int index) => new(account, 0, index);
    public static DerivationPath Internal(int account, int index) => new(account, 1, index);

    public override string ToString() => $"m/44'/{CoinType}'/{Account}'/{Change}/{Index}";

    public Dictionary<string, int> ToDict() => new()
    {
        ["account"] = Account,
        ["change"] = Change,
        ["index"] = Index
    };
}

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

/// <summary>Hybrid public key combining Ed25519 and Dilithium3.</summary>
public record HybridPublicKey
{
    public string Ed25519 { get; init; } = "";
    public string Dilithium { get; init; } = "";

    public byte[] Ed25519Bytes => Convert.FromBase64String(Ed25519);
    public byte[] DilithiumBytes => Convert.FromBase64String(Dilithium);
    public int Size => Ed25519Bytes.Length + DilithiumBytes.Length;

    public Dictionary<string, string> ToDict() => new()
    {
        ["ed25519"] = Ed25519,
        ["dilithium"] = Dilithium
    };
}

/// <summary>Secret key for hybrid signatures.</summary>
public record SecretKey
{
    [JsonPropertyName("ed25519_seed")]
    public string Ed25519Seed { get; init; } = "";

    [JsonPropertyName("master_seed")]
    public string MasterSeed { get; init; } = "";

    public byte[] Ed25519SeedBytes => Convert.FromBase64String(Ed25519Seed);
    public byte[] MasterSeedBytes => Convert.FromBase64String(MasterSeed);

    public Dictionary<string, string> ToDict() => new()
    {
        ["ed25519_seed"] = Ed25519Seed,
        ["master_seed"] = MasterSeed
    };
}

/// <summary>Falcon public key.</summary>
public record FalconPublicKey
{
    public string Variant { get; init; } = "";
    public string Bytes { get; init; } = "";

    public FalconVariant VariantEnum => Variant.ToLower() switch
    {
        "falcon512" => FalconVariant.Falcon512,
        "falcon1024" => FalconVariant.Falcon1024,
        _ => FalconVariant.Falcon512
    };

    public byte[] KeyBytes => Convert.FromBase64String(Bytes);
}

/// <summary>Falcon secret key.</summary>
public record FalconSecretKey
{
    public string Variant { get; init; } = "";
    public string Bytes { get; init; } = "";

    public FalconVariant VariantEnum => Variant.ToLower() switch
    {
        "falcon512" => FalconVariant.Falcon512,
        "falcon1024" => FalconVariant.Falcon1024,
        _ => FalconVariant.Falcon512
    };

    public byte[] KeyBytes => Convert.FromBase64String(Bytes);
}

/// <summary>SPHINCS+ public key.</summary>
public record SphincsPublicKey
{
    public string Variant { get; init; } = "";
    public string Bytes { get; init; } = "";

    public SphincsVariant VariantEnum => Variant.ToLower() switch
    {
        "shake128s" => SphincsVariant.Shake128s,
        "shake192s" => SphincsVariant.Shake192s,
        "shake256s" => SphincsVariant.Shake256s,
        _ => SphincsVariant.Shake128s
    };

    public byte[] KeyBytes => Convert.FromBase64String(Bytes);
}

/// <summary>SPHINCS+ secret key.</summary>
public record SphincsSecretKey
{
    public string Variant { get; init; } = "";
    public string Bytes { get; init; } = "";

    public SphincsVariant VariantEnum => Variant.ToLower() switch
    {
        "shake128s" => SphincsVariant.Shake128s,
        "shake192s" => SphincsVariant.Shake192s,
        "shake256s" => SphincsVariant.Shake256s,
        _ => SphincsVariant.Shake128s
    };

    public byte[] KeyBytes => Convert.FromBase64String(Bytes);
}

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

/// <summary>Hybrid signature combining Ed25519 and Dilithium3.</summary>
public record HybridSignature
{
    public string Ed25519 { get; init; } = "";
    public string Dilithium { get; init; } = "";

    public byte[] Ed25519Bytes => Convert.FromBase64String(Ed25519);
    public byte[] DilithiumBytes => Convert.FromBase64String(Dilithium);
    public int Size => Ed25519Bytes.Length + DilithiumBytes.Length;

    public byte[] ToBytes()
    {
        var e = Ed25519Bytes;
        var d = DilithiumBytes;
        var result = new byte[e.Length + d.Length];
        Buffer.BlockCopy(e, 0, result, 0, e.Length);
        Buffer.BlockCopy(d, 0, result, e.Length, d.Length);
        return result;
    }

    public Dictionary<string, string> ToDict() => new()
    {
        ["ed25519"] = Ed25519,
        ["dilithium"] = Dilithium
    };
}

/// <summary>Falcon signature.</summary>
public record FalconSignature
{
    public string Variant { get; init; } = "";
    public string Signature { get; init; } = "";

    public FalconVariant VariantEnum => Variant.ToLower() switch
    {
        "falcon512" => FalconVariant.Falcon512,
        "falcon1024" => FalconVariant.Falcon1024,
        _ => FalconVariant.Falcon512
    };

    public byte[] SignatureBytes => Convert.FromBase64String(Signature);
    public int Size => SignatureBytes.Length;
}

/// <summary>SPHINCS+ signature.</summary>
public record SphincsSignature
{
    public string Variant { get; init; } = "";
    public string Signature { get; init; } = "";

    public SphincsVariant VariantEnum => Variant.ToLower() switch
    {
        "shake128s" => SphincsVariant.Shake128s,
        "shake192s" => SphincsVariant.Shake192s,
        "shake256s" => SphincsVariant.Shake256s,
        _ => SphincsVariant.Shake128s
    };

    public byte[] SignatureBytes => Convert.FromBase64String(Signature);
    public int Size => SignatureBytes.Length;
}

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

/// <summary>Hybrid keypair for Ed25519 + Dilithium3.</summary>
public record HybridKeypair
{
    [JsonPropertyName("public_key")]
    public HybridPublicKey PublicKey { get; init; } = new();

    [JsonPropertyName("secret_key")]
    public SecretKey SecretKey { get; init; } = new();

    public Dictionary<string, string> Addresses { get; init; } = new();

    public string GetAddress(Network network) =>
        Addresses.TryGetValue(network.ToApiString(), out var addr) ? addr : "";
}

/// <summary>Falcon keypair.</summary>
public record FalconKeypair
{
    public string Variant { get; init; } = "";

    [JsonPropertyName("public_key")]
    public FalconPublicKey PublicKey { get; init; } = new();

    [JsonPropertyName("secret_key")]
    public FalconSecretKey SecretKey { get; init; } = new();

    public FalconVariant VariantEnum => PublicKey.VariantEnum;
}

/// <summary>SPHINCS+ keypair.</summary>
public record SphincsKeypair
{
    public string Variant { get; init; } = "";

    [JsonPropertyName("public_key")]
    public SphincsPublicKey PublicKey { get; init; } = new();

    [JsonPropertyName("secret_key")]
    public SphincsSecretKey SecretKey { get; init; } = new();

    public SphincsVariant VariantEnum => PublicKey.VariantEnum;
}

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

/// <summary>BIP-39 mnemonic phrase.</summary>
public record Mnemonic
{
    public string Phrase { get; init; } = "";
    public List<string>? Words { get; init; }

    [JsonPropertyName("word_count")]
    public int WordCount { get; init; }

    public string? Entropy { get; init; }

    public List<string> GetWords() => Words ?? new List<string>(Phrase.Split(' '));
    public int GetWordCount() => WordCount > 0 ? WordCount : GetWords().Count;
    public byte[] GetEntropy() => Entropy != null ? Convert.FromBase64String(Entropy) : Array.Empty<byte>();
}

/// <summary>Mnemonic validation result.</summary>
public record MnemonicValidation
{
    public bool Valid { get; init; }
    public string? Error { get; init; }
}

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

/// <summary>Blockchain address.</summary>
public record Address
{
    [JsonPropertyName("address")]
    public string AddressString { get; init; } = "";

    public string NetworkString { get; init; } = "";

    [JsonPropertyName("pubkey_hash")]
    public string? PubkeyHash { get; init; }

    public Network Network => NetworkExtensions.FromApiString(NetworkString);
    public byte[] PubkeyHashBytes => PubkeyHash != null ? Convert.FromBase64String(PubkeyHash) : Array.Empty<byte>();
}

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

/// <summary>256-bit hash result.</summary>
public record Hash256
{
    public string Hash { get; init; } = "";

    public string Hex => Hash;

    public byte[] Bytes
    {
        get
        {
            var result = new byte[Hash.Length / 2];
            for (int i = 0; i < result.Length; i++)
            {
                result[i] = Convert.ToByte(Hash.Substring(i * 2, 2), 16);
            }
            return result;
        }
    }
}

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

/// <summary>Crypto SDK exception.</summary>
public class CryptoException : Exception
{
    public string? Code { get; }
    public int? HttpStatus { get; }

    public CryptoException(string message, string? code = null, int? httpStatus = null)
        : base(message)
    {
        Code = code;
        HttpStatus = httpStatus;
    }
}

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

/// <summary>Cryptographic constants.</summary>
public static class CryptoConstants
{
    public const int Ed25519PublicKeySize = 32;
    public const int Ed25519SecretKeySize = 32;
    public const int Ed25519SignatureSize = 64;
    public const int Dilithium3PublicKeySize = 1952;
    public const int Dilithium3SignatureSize = 3293;
    public const int HybridSignatureSize = 64 + 3293;
    public const int CoinType = 0x5359;
    public const int MinPbkdf2Iterations = 10000;
    public const int MinSaltLength = 8;
    public const string DefaultEndpoint = "https://crypto.synor.io/v1";
}
