import Foundation

// MARK: - Enumerations

/// Network type for the Synor blockchain.
public enum Network: String, Codable, CaseIterable {
    case mainnet
    case testnet
    case devnet
}

/// Falcon signature variant.
public enum FalconVariant: String, Codable, CaseIterable {
    case falcon512
    case falcon1024

    public var signatureSize: Int {
        switch self {
        case .falcon512: return 690
        case .falcon1024: return 1330
        }
    }

    public var publicKeySize: Int {
        switch self {
        case .falcon512: return 897
        case .falcon1024: return 1793
        }
    }

    public var securityLevel: Int {
        switch self {
        case .falcon512: return 128
        case .falcon1024: return 256
        }
    }
}

/// SPHINCS+ signature variant.
public enum SphincsVariant: String, Codable, CaseIterable {
    case shake128s
    case shake192s
    case shake256s

    public var signatureSize: Int {
        switch self {
        case .shake128s: return 7856
        case .shake192s: return 16224
        case .shake256s: return 29792
        }
    }

    public var securityLevel: Int {
        switch self {
        case .shake128s: return 128
        case .shake192s: return 192
        case .shake256s: return 256
        }
    }
}

/// Post-quantum algorithm types.
public enum PqAlgorithm: String, Codable {
    case dilithium3
    case falcon512
    case falcon1024
    case sphincs128s
    case sphincs192s
    case sphincs256s
}

/// Cryptographic algorithm family.
public enum AlgorithmFamily: String, Codable {
    case classical
    case lattice
    case hashBased = "hash_based"
    case hybrid
}

// MARK: - Configuration Types

/// Configuration for the Crypto SDK.
public struct CryptoConfig {
    public let apiKey: String
    public var endpoint: String
    public var timeout: TimeInterval
    public var retries: Int
    public var debug: Bool
    public var defaultNetwork: Network

    public init(
        apiKey: String,
        endpoint: String = "https://crypto.synor.io/v1",
        timeout: TimeInterval = 30.0,
        retries: Int = 3,
        debug: Bool = false,
        defaultNetwork: Network = .mainnet
    ) {
        self.apiKey = apiKey
        self.endpoint = endpoint
        self.timeout = timeout
        self.retries = retries
        self.debug = debug
        self.defaultNetwork = defaultNetwork
    }
}

/// Configuration for key derivation.
public struct DerivationConfig {
    public var salt: Data?
    public var info: Data?
    public var outputLength: Int

    public init(salt: Data? = nil, info: Data? = nil, outputLength: Int = 32) {
        self.salt = salt
        self.info = info
        self.outputLength = outputLength
    }
}

/// Configuration for password-based key derivation.
public struct PasswordDerivationConfig {
    public let salt: Data
    public var iterations: Int
    public var outputLength: Int

    public init(salt: Data, iterations: Int = 100000, outputLength: Int = 32) {
        self.salt = salt
        self.iterations = iterations
        self.outputLength = outputLength
    }
}

/// BIP-44 derivation path.
public struct DerivationPath: CustomStringConvertible {
    public static let coinType = 0x5359

    public let account: Int
    public let change: Int
    public let index: Int

    public init(account: Int, change: Int, index: Int) {
        self.account = account
        self.change = change
        self.index = index
    }

    public static func external(account: Int, index: Int) -> DerivationPath {
        DerivationPath(account: account, change: 0, index: index)
    }

    public static func `internal`(account: Int, index: Int) -> DerivationPath {
        DerivationPath(account: account, change: 1, index: index)
    }

    public var description: String {
        "m/44'/\(Self.coinType)'/\(account)'/\(change)/\(index)"
    }

    public func toDict() -> [String: Int] {
        ["account": account, "change": change, "index": index]
    }
}

// MARK: - Key Types

/// Hybrid public key combining Ed25519 and Dilithium3.
public struct HybridPublicKey: Codable {
    public let ed25519: String
    public let dilithium: String

    public var ed25519Bytes: Data {
        Data(base64Encoded: ed25519) ?? Data()
    }

    public var dilithiumBytes: Data {
        Data(base64Encoded: dilithium) ?? Data()
    }

    public var size: Int {
        ed25519Bytes.count + dilithiumBytes.count
    }

    public func toDict() -> [String: String] {
        ["ed25519": ed25519, "dilithium": dilithium]
    }
}

/// Secret key for hybrid signatures.
public struct SecretKey: Codable {
    public let ed25519Seed: String
    public let masterSeed: String

    enum CodingKeys: String, CodingKey {
        case ed25519Seed = "ed25519_seed"
        case masterSeed = "master_seed"
    }

    public var ed25519SeedBytes: Data {
        Data(base64Encoded: ed25519Seed) ?? Data()
    }

    public var masterSeedBytes: Data {
        Data(base64Encoded: masterSeed) ?? Data()
    }

    public func toDict() -> [String: String] {
        ["ed25519_seed": ed25519Seed, "master_seed": masterSeed]
    }
}

/// Falcon public key.
public struct FalconPublicKey: Codable {
    public let variant: String
    public let bytes: String

    public var variantEnum: FalconVariant {
        FalconVariant(rawValue: variant) ?? .falcon512
    }

    public var keyBytes: Data {
        Data(base64Encoded: bytes) ?? Data()
    }
}

/// Falcon secret key.
public struct FalconSecretKey: Codable {
    public let variant: String
    public let bytes: String

    public var variantEnum: FalconVariant {
        FalconVariant(rawValue: variant) ?? .falcon512
    }

    public var keyBytes: Data {
        Data(base64Encoded: bytes) ?? Data()
    }
}

/// SPHINCS+ public key.
public struct SphincsPublicKey: Codable {
    public let variant: String
    public let bytes: String

    public var variantEnum: SphincsVariant {
        SphincsVariant(rawValue: variant) ?? .shake128s
    }

    public var keyBytes: Data {
        Data(base64Encoded: bytes) ?? Data()
    }
}

/// SPHINCS+ secret key.
public struct SphincsSecretKey: Codable {
    public let variant: String
    public let bytes: String

    public var variantEnum: SphincsVariant {
        SphincsVariant(rawValue: variant) ?? .shake128s
    }

    public var keyBytes: Data {
        Data(base64Encoded: bytes) ?? Data()
    }
}

// MARK: - Signature Types

/// Hybrid signature combining Ed25519 and Dilithium3.
public struct HybridSignature: Codable {
    public let ed25519: String
    public let dilithium: String

    public var ed25519Bytes: Data {
        Data(base64Encoded: ed25519) ?? Data()
    }

    public var dilithiumBytes: Data {
        Data(base64Encoded: dilithium) ?? Data()
    }

    public var size: Int {
        ed25519Bytes.count + dilithiumBytes.count
    }

    public var bytes: Data {
        var result = Data()
        result.append(ed25519Bytes)
        result.append(dilithiumBytes)
        return result
    }

    public func toDict() -> [String: String] {
        ["ed25519": ed25519, "dilithium": dilithium]
    }
}

/// Falcon signature.
public struct FalconSignature: Codable {
    public let variant: String
    public let signature: String

    public var variantEnum: FalconVariant {
        FalconVariant(rawValue: variant) ?? .falcon512
    }

    public var signatureBytes: Data {
        Data(base64Encoded: signature) ?? Data()
    }

    public var size: Int {
        signatureBytes.count
    }
}

/// SPHINCS+ signature.
public struct SphincsSignature: Codable {
    public let variant: String
    public let signature: String

    public var variantEnum: SphincsVariant {
        SphincsVariant(rawValue: variant) ?? .shake128s
    }

    public var signatureBytes: Data {
        Data(base64Encoded: signature) ?? Data()
    }

    public var size: Int {
        signatureBytes.count
    }
}

// MARK: - Keypair Types

/// Hybrid keypair for Ed25519 + Dilithium3.
public struct HybridKeypair: Codable {
    public let publicKey: HybridPublicKey
    public let secretKey: SecretKey
    public let addresses: [String: String]

    enum CodingKeys: String, CodingKey {
        case publicKey = "public_key"
        case secretKey = "secret_key"
        case addresses
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        publicKey = try container.decode(HybridPublicKey.self, forKey: .publicKey)
        secretKey = try container.decode(SecretKey.self, forKey: .secretKey)
        addresses = try container.decodeIfPresent([String: String].self, forKey: .addresses) ?? [:]
    }

    public func address(for network: Network) -> String {
        addresses[network.rawValue] ?? ""
    }
}

/// Falcon keypair.
public struct FalconKeypair: Codable {
    public let variant: String
    public let publicKey: FalconPublicKey
    public let secretKey: FalconSecretKey

    enum CodingKeys: String, CodingKey {
        case variant
        case publicKey = "public_key"
        case secretKey = "secret_key"
    }

    public var variantEnum: FalconVariant {
        FalconVariant(rawValue: variant) ?? .falcon512
    }
}

/// SPHINCS+ keypair.
public struct SphincsKeypair: Codable {
    public let variant: String
    public let publicKey: SphincsPublicKey
    public let secretKey: SphincsSecretKey

    enum CodingKeys: String, CodingKey {
        case variant
        case publicKey = "public_key"
        case secretKey = "secret_key"
    }

    public var variantEnum: SphincsVariant {
        SphincsVariant(rawValue: variant) ?? .shake128s
    }
}

// MARK: - Mnemonic Types

/// BIP-39 mnemonic phrase.
public struct Mnemonic: Codable {
    public let phrase: String
    public let words: [String]?
    public let wordCount: Int?
    public let entropy: String?

    enum CodingKeys: String, CodingKey {
        case phrase
        case words
        case wordCount = "word_count"
        case entropy
    }

    public var wordList: [String] {
        words ?? phrase.split(separator: " ").map(String.init)
    }

    public var count: Int {
        wordCount ?? wordList.count
    }

    public var entropyBytes: Data {
        entropy.flatMap { Data(base64Encoded: $0) } ?? Data()
    }
}

/// Mnemonic validation result.
public struct MnemonicValidation: Codable {
    public let valid: Bool
    public let error: String?
}

// MARK: - Address Types

/// Blockchain address.
public struct Address: Codable {
    public let address: String
    public let network: String
    public let pubkeyHash: String?

    enum CodingKeys: String, CodingKey {
        case address
        case network
        case pubkeyHash = "pubkey_hash"
    }

    public var networkEnum: Network {
        Network(rawValue: network) ?? .mainnet
    }

    public var pubkeyHashBytes: Data {
        pubkeyHash.flatMap { Data(base64Encoded: $0) } ?? Data()
    }
}

// MARK: - Hash Types

/// 256-bit hash result.
public struct Hash256: Codable {
    public let hash: String

    public var hex: String { hash }

    public var bytes: Data {
        var result = Data()
        var index = hash.startIndex
        while index < hash.endIndex {
            let nextIndex = hash.index(index, offsetBy: 2)
            if let byte = UInt8(hash[index..<nextIndex], radix: 16) {
                result.append(byte)
            }
            index = nextIndex
        }
        return result
    }
}

// MARK: - Error Types

/// Crypto SDK error.
public struct CryptoError: Error, LocalizedError {
    public let message: String
    public let code: String?

    public var errorDescription: String? { message }
}

// MARK: - Constants

/// Cryptographic constants.
public enum CryptoConstants {
    public static let ed25519PublicKeySize = 32
    public static let ed25519SecretKeySize = 32
    public static let ed25519SignatureSize = 64
    public static let dilithium3PublicKeySize = 1952
    public static let dilithium3SignatureSize = 3293
    public static let hybridSignatureSize = 64 + 3293
    public static let coinType = 0x5359
    public static let minPbkdf2Iterations = 10000
    public static let minSaltLength = 8
    public static let defaultEndpoint = "https://crypto.synor.io/v1"
}
