import Foundation
import CryptoKit

/// X25519 Diffie-Hellman key agreement
enum X25519Crypto {

    // MARK: - Key Generation

    /// Generate X25519 keypair
    static func generateKeypair() -> (privateKey: Curve25519.KeyAgreement.PrivateKey, publicKey: Curve25519.KeyAgreement.PublicKey) {
        let privateKey = Curve25519.KeyAgreement.PrivateKey()
        return (privateKey, privateKey.publicKey)
    }

    // MARK: - Serialization

    /// Serialize X25519 private key to 32 raw bytes
    static func serializePrivate(_ key: Curve25519.KeyAgreement.PrivateKey) -> Data {
        key.rawData  // 32 bytes
    }

    /// Serialize X25519 public key to 32 raw bytes
    static func serializePublic(_ key: Curve25519.KeyAgreement.PublicKey) -> Data {
        key.rawData  // 32 bytes
    }

    /// Load X25519 private key from 32 raw bytes
    static func loadPrivate(_ data: Data) throws -> Curve25519.KeyAgreement.PrivateKey {
        guard data.count == 32 else {
            throw CryptoError.invalidKeyData("X25519 private key must be 32 bytes")
        }
        return try Curve25519.KeyAgreement.PrivateKey(rawRepresentation: data)
    }

    /// Load X25519 public key from 32 raw bytes
    static func loadPublic(_ data: Data) throws -> Curve25519.KeyAgreement.PublicKey {
        guard data.count == 32 else {
            throw CryptoError.invalidKeyData("X25519 public key must be 32 bytes")
        }
        return try Curve25519.KeyAgreement.PublicKey(rawRepresentation: data)
    }

    // MARK: - Diffie-Hellman

    /// Perform X25519 DH key agreement. Returns 32-byte shared secret.
    /// Matches Python: x25519_dh(private_key, public_key)
    static func dh(_ privateKey: Curve25519.KeyAgreement.PrivateKey, _ publicKey: Curve25519.KeyAgreement.PublicKey) throws -> Data {
        let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: publicKey)
        // Extract raw bytes from SharedSecret
        return sharedSecret.withUnsafeBytes { Data($0) }
    }

    // MARK: - Ed25519 → X25519 Key Conversion

    /// Convert Ed25519 private key to X25519 private key.
    /// SHA-512(seed) → take first 32 bytes → clamp per RFC 7748
    /// Matches Python: ed25519_private_to_x25519(ed_private)
    static func fromEd25519Private(_ edPrivate: Curve25519.Signing.PrivateKey) throws -> Curve25519.KeyAgreement.PrivateKey {
        let raw = edPrivate.rawData  // 32 bytes seed
        // SHA-512 of the seed
        let hash = SHA512.hash(data: raw)
        var clamped = Data(hash.prefix(32))
        // Clamp per RFC 7748
        clamped[0] &= 248
        clamped[31] &= 127
        clamped[31] |= 64
        return try Curve25519.KeyAgreement.PrivateKey(rawRepresentation: clamped)
    }

    /// Convert Ed25519 public key to X25519 public key.
    /// Uses Montgomery birational map: u = (1+y)/(1-y) mod p
    /// Matches Python: ed25519_public_to_x25519(ed_public)
    static func fromEd25519Public(_ edPublic: Curve25519.Signing.PublicKey) throws -> Curve25519.KeyAgreement.PublicKey {
        let x25519Bytes = FieldArithmetic.ed25519PublicToX25519(edPublic.rawData)
        return try Curve25519.KeyAgreement.PublicKey(rawRepresentation: x25519Bytes)
    }
}
