"""
Synor Crypto SDK Types

Quantum-resistant cryptographic types for the Synor blockchain.
"""

from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional, Dict, Callable
import base64


# ============================================================================
# Enumerations
# ============================================================================

class Network(str, Enum):
    """Network type for address generation."""
    MAINNET = "mainnet"
    TESTNET = "testnet"
    DEVNET = "devnet"


class FalconVariant(str, Enum):
    """Falcon variant selection."""
    FALCON512 = "falcon512"   # 128-bit security, ~690 byte signatures
    FALCON1024 = "falcon1024"  # 256-bit security, ~1330 byte signatures


class SphincsVariant(str, Enum):
    """SPHINCS+ variant selection."""
    SHAKE128S = "shake128s"  # 128-bit security, ~7.8KB signatures
    SHAKE192S = "shake192s"  # 192-bit security, ~16KB signatures
    SHAKE256S = "shake256s"  # 256-bit security, ~30KB signatures


class PqAlgorithm(str, Enum):
    """Post-quantum algorithm selection."""
    DILITHIUM3 = "dilithium3"
    FALCON512 = "falcon512"
    FALCON1024 = "falcon1024"
    SPHINCS128S = "sphincs128s"
    SPHINCS192S = "sphincs192s"
    SPHINCS256S = "sphincs256s"


class AlgorithmFamily(str, Enum):
    """Algorithm family classification."""
    CLASSICAL = "classical"
    LATTICE = "lattice"
    HASH_BASED = "hash_based"
    HYBRID = "hybrid"


# ============================================================================
# Configuration Types
# ============================================================================

@dataclass
class CryptoConfig:
    """Crypto SDK configuration."""
    api_key: str
    endpoint: str = "https://crypto.synor.io/v1"
    timeout: int = 30000
    retries: int = 3
    debug: bool = False
    default_network: Network = Network.MAINNET


@dataclass
class DerivationConfig:
    """Key derivation configuration."""
    salt: Optional[bytes] = None
    info: Optional[bytes] = None
    output_length: int = 32


@dataclass
class PasswordDerivationConfig:
    """Password derivation configuration."""
    salt: bytes = field(default_factory=bytes)
    iterations: int = 100000
    output_length: int = 32


@dataclass
class DerivationPath:
    """BIP-44 derivation path."""
    account: int = 0
    change: int = 0
    index: int = 0

    COIN_TYPE: int = 0x5359  # Synor coin type

    def __str__(self) -> str:
        return f"m/44'/{self.COIN_TYPE}'/{self.account}'/{self.change}/{self.index}"

    @classmethod
    def external(cls, account: int, index: int) -> 'DerivationPath':
        """Creates a path for external addresses."""
        return cls(account=account, change=0, index=index)

    @classmethod
    def internal(cls, account: int, index: int) -> 'DerivationPath':
        """Creates a path for internal (change) addresses."""
        return cls(account=account, change=1, index=index)


# ============================================================================
# Key Types
# ============================================================================

@dataclass
class HybridPublicKey:
    """Hybrid public key (Ed25519 + Dilithium3)."""
    ed25519: bytes  # 32 bytes
    dilithium: bytes  # ~1952 bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'HybridPublicKey':
        return cls(
            ed25519=base64.b64decode(data['ed25519']),
            dilithium=base64.b64decode(data['dilithium'])
        )

    def to_dict(self) -> dict:
        return {
            'ed25519': base64.b64encode(self.ed25519).decode(),
            'dilithium': base64.b64encode(self.dilithium).decode()
        }

    def size(self) -> int:
        """Returns the total size in bytes."""
        return len(self.ed25519) + len(self.dilithium)


@dataclass
class SecretKey:
    """Secret key (master seed)."""
    ed25519_seed: bytes  # 32 bytes
    master_seed: bytes   # 64 bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'SecretKey':
        return cls(
            ed25519_seed=base64.b64decode(data['ed25519_seed']),
            master_seed=base64.b64decode(data['master_seed'])
        )


@dataclass
class FalconPublicKey:
    """Falcon public key."""
    variant: FalconVariant
    bytes: bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'FalconPublicKey':
        return cls(
            variant=FalconVariant(data['variant']),
            bytes=base64.b64decode(data['bytes'])
        )


@dataclass
class FalconSecretKey:
    """Falcon secret key."""
    variant: FalconVariant
    bytes: bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'FalconSecretKey':
        return cls(
            variant=FalconVariant(data['variant']),
            bytes=base64.b64decode(data['bytes'])
        )


@dataclass
class SphincsPublicKey:
    """SPHINCS+ public key."""
    variant: SphincsVariant
    bytes: bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'SphincsPublicKey':
        return cls(
            variant=SphincsVariant(data['variant']),
            bytes=base64.b64decode(data['bytes'])
        )


@dataclass
class SphincsSecretKey:
    """SPHINCS+ secret key."""
    variant: SphincsVariant
    bytes: bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'SphincsSecretKey':
        return cls(
            variant=SphincsVariant(data['variant']),
            bytes=base64.b64decode(data['bytes'])
        )


# ============================================================================
# Signature Types
# ============================================================================

@dataclass
class HybridSignature:
    """Hybrid signature (Ed25519 + Dilithium3)."""
    ed25519: bytes   # 64 bytes
    dilithium: bytes  # ~3293 bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'HybridSignature':
        return cls(
            ed25519=base64.b64decode(data['ed25519']),
            dilithium=base64.b64decode(data['dilithium'])
        )

    def to_bytes(self) -> bytes:
        """Serializes the signature to bytes."""
        return self.ed25519 + self.dilithium

    @classmethod
    def from_bytes(cls, data: bytes) -> 'HybridSignature':
        """Deserializes a signature from bytes."""
        if len(data) < 64:
            raise ValueError("Invalid signature length")
        return cls(
            ed25519=data[:64],
            dilithium=data[64:]
        )

    def size(self) -> int:
        """Returns the total size in bytes."""
        return len(self.ed25519) + len(self.dilithium)


@dataclass
class FalconSignature:
    """Falcon signature."""
    variant: FalconVariant
    bytes: bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'FalconSignature':
        return cls(
            variant=FalconVariant(data['variant']),
            bytes=base64.b64decode(data['signature'])
        )

    def size(self) -> int:
        return len(self.bytes)


@dataclass
class SphincsSignature:
    """SPHINCS+ signature."""
    variant: SphincsVariant
    bytes: bytes

    @classmethod
    def from_dict(cls, data: dict) -> 'SphincsSignature':
        return cls(
            variant=SphincsVariant(data['variant']),
            bytes=base64.b64decode(data['signature'])
        )

    def size(self) -> int:
        return len(self.bytes)


# ============================================================================
# Keypair Types
# ============================================================================

@dataclass
class HybridKeypair:
    """Hybrid keypair (Ed25519 + Dilithium3)."""
    public_key: HybridPublicKey
    secret_key: SecretKey
    _addresses: Dict[str, str] = field(default_factory=dict)

    def address(self, network: Network) -> str:
        """Returns the address for the given network."""
        return self._addresses.get(network.value, "")

    @classmethod
    def from_dict(cls, data: dict) -> 'HybridKeypair':
        return cls(
            public_key=HybridPublicKey.from_dict(data['public_key']),
            secret_key=SecretKey.from_dict(data['secret_key']),
            _addresses=data.get('addresses', {})
        )


@dataclass
class FalconKeypair:
    """Falcon keypair."""
    variant: FalconVariant
    public_key: FalconPublicKey
    secret_key: FalconSecretKey

    @classmethod
    def from_dict(cls, data: dict) -> 'FalconKeypair':
        return cls(
            variant=FalconVariant(data['variant']),
            public_key=FalconPublicKey.from_dict(data['public_key']),
            secret_key=FalconSecretKey.from_dict(data['secret_key'])
        )


@dataclass
class SphincsKeypair:
    """SPHINCS+ keypair."""
    variant: SphincsVariant
    public_key: SphincsPublicKey
    secret_key: SphincsSecretKey

    @classmethod
    def from_dict(cls, data: dict) -> 'SphincsKeypair':
        return cls(
            variant=SphincsVariant(data['variant']),
            public_key=SphincsPublicKey.from_dict(data['public_key']),
            secret_key=SphincsSecretKey.from_dict(data['secret_key'])
        )


# ============================================================================
# Mnemonic Types
# ============================================================================

@dataclass
class Mnemonic:
    """BIP-39 mnemonic phrase."""
    phrase: str
    words: List[str] = field(default_factory=list)
    word_count: int = 24
    entropy: bytes = field(default_factory=bytes)

    def __post_init__(self):
        if not self.words:
            self.words = self.phrase.split()
        if not self.word_count:
            self.word_count = len(self.words)

    @classmethod
    def from_dict(cls, data: dict) -> 'Mnemonic':
        return cls(
            phrase=data['phrase'],
            words=data.get('words', data['phrase'].split()),
            word_count=data.get('word_count', len(data['phrase'].split())),
            entropy=base64.b64decode(data.get('entropy', '')) if data.get('entropy') else b''
        )


@dataclass
class MnemonicValidation:
    """Mnemonic validation result."""
    valid: bool
    error: Optional[str] = None

    @classmethod
    def from_dict(cls, data: dict) -> 'MnemonicValidation':
        return cls(
            valid=data['valid'],
            error=data.get('error')
        )


# ============================================================================
# Address Types
# ============================================================================

@dataclass
class Address:
    """Blockchain address."""
    address: str
    network: Network
    pubkey_hash: bytes = field(default_factory=bytes)

    @classmethod
    def from_dict(cls, data: dict) -> 'Address':
        return cls(
            address=data['address'],
            network=Network(data['network']),
            pubkey_hash=base64.b64decode(data.get('pubkey_hash', '')) if data.get('pubkey_hash') else b''
        )


# ============================================================================
# Hash Types
# ============================================================================

@dataclass
class Hash256:
    """256-bit hash."""
    bytes: bytes
    hex: str

    @classmethod
    def from_dict(cls, data: dict) -> 'Hash256':
        hex_str = data['hash']
        return cls(
            bytes=bytes.fromhex(hex_str),
            hex=hex_str
        )


# ============================================================================
# Negotiation Types
# ============================================================================

@dataclass
class AlgorithmCapabilities:
    """Algorithm capabilities for negotiation."""
    pq_algorithms: List[PqAlgorithm]
    classical: bool = True
    hybrid: bool = True
    preferred: Optional[PqAlgorithm] = None

    @classmethod
    def from_dict(cls, data: dict) -> 'AlgorithmCapabilities':
        return cls(
            pq_algorithms=[PqAlgorithm(a) for a in data.get('pq_algorithms', [])],
            classical=data.get('classical', True),
            hybrid=data.get('hybrid', True),
            preferred=PqAlgorithm(data['preferred']) if data.get('preferred') else None
        )


@dataclass
class NegotiationPolicy:
    """Negotiation policy."""
    min_security_level: int = 128
    prefer_compact: bool = False
    allow_classical: bool = False
    required_families: List[AlgorithmFamily] = field(default_factory=list)


@dataclass
class NegotiationResult:
    """Negotiation result."""
    algorithm: PqAlgorithm
    security_level: int
    family: AlgorithmFamily
    hybrid: bool

    @classmethod
    def from_dict(cls, data: dict) -> 'NegotiationResult':
        return cls(
            algorithm=PqAlgorithm(data['algorithm']),
            security_level=data['security_level'],
            family=AlgorithmFamily(data['family']),
            hybrid=data['hybrid']
        )


@dataclass
class SessionParams:
    """Session parameters after negotiation."""
    algorithm: PqAlgorithm
    session_key: Optional[bytes] = None
    expires_at: Optional[int] = None

    @classmethod
    def from_dict(cls, data: dict) -> 'SessionParams':
        return cls(
            algorithm=PqAlgorithm(data['algorithm']),
            session_key=base64.b64decode(data['session_key']) if data.get('session_key') else None,
            expires_at=data.get('expires_at')
        )


# ============================================================================
# Error Types
# ============================================================================

class CryptoError(Exception):
    """Crypto operation error."""

    def __init__(self, message: str, code: Optional[str] = None):
        super().__init__(message)
        self.code = code


# ============================================================================
# Constants
# ============================================================================

class CryptoConstants:
    """Crypto constants."""
    ED25519_PUBLIC_KEY_SIZE = 32
    ED25519_SECRET_KEY_SIZE = 32
    ED25519_SIGNATURE_SIZE = 64
    DILITHIUM3_PUBLIC_KEY_SIZE = 1952
    DILITHIUM3_SIGNATURE_SIZE = 3293
    HYBRID_SIGNATURE_SIZE = 64 + 3293
    FALCON512_SIGNATURE_SIZE = 690
    FALCON512_PUBLIC_KEY_SIZE = 897
    FALCON1024_SIGNATURE_SIZE = 1330
    FALCON1024_PUBLIC_KEY_SIZE = 1793
    SPHINCS128S_SIGNATURE_SIZE = 7856
    SPHINCS192S_SIGNATURE_SIZE = 16224
    SPHINCS256S_SIGNATURE_SIZE = 29792
    COIN_TYPE = 0x5359
    MIN_PBKDF2_ITERATIONS = 10000
    MIN_SALT_LENGTH = 8
    DEFAULT_ENDPOINT = "https://crypto.synor.io/v1"


# Algorithm size comparison
ALGORITHM_SIZES = {
    'ed25519': {'signature': 64, 'public_key': 32},
    'dilithium3': {'signature': 3293, 'public_key': 1952},
    'hybrid': {'signature': 3357, 'public_key': 1984},
    'falcon512': {'signature': 690, 'public_key': 897},
    'falcon1024': {'signature': 1330, 'public_key': 1793},
    'sphincs128s': {'signature': 7856, 'public_key': 32},
    'sphincs192s': {'signature': 16224, 'public_key': 48},
    'sphincs256s': {'signature': 29792, 'public_key': 64},
}
