import os
import base64
import ascon
import hashlib
import secrets
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
from typing import Tuple

def ascon_key_generator() -> str:
    key = os.urandom(16)
    return key.hex()

def encrypt(key_hex: str, plaintext: str) -> str:
    key = bytes.fromhex(key_hex)
    nonce = os.urandom(16)
    ad = b''

    ciphertext = ascon.encrypt(key, nonce, ad, plaintext.encode(), variant="Ascon-AEAD128")

    combined = nonce + ciphertext
    data_b64 = base64.b64encode(combined).decode()

    return data_b64

def decrypt(key_hex: str, data_b64: str) -> str:
    key = bytes.fromhex(key_hex)
    combined = base64.b64decode(data_b64)

    nonce = combined[:16]
    ciphertext = combined[16:]

    plaintext = ascon.decrypt(key, nonce, b'', ciphertext, variant="Ascon-AEAD128")
    return plaintext.decode()

def hash_password(password: str) -> str:
    salt = secrets.token_hex(16)
    hashed = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 100_000)
    return f"{salt}${hashed.hex()}"

def check_password(password: str, stored: str) -> bool:
    salt, hashed = stored.split('$')
    new_hash = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 100_000)
    return new_hash.hex() == hashed

def salt_randomizer() -> bytes:
    return secrets.token_bytes(32)

def hkdf_sha256(salt: bytes, shared_secret_raw: str) -> str:
    hkdf = HKDF(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        info=b'',
    )
    key_material = hkdf.derive(shared_secret_raw)

    return key_material

def compute_shared_secret(secret_key: bytes, public_key: bytes) -> bytes:
    secret_key_bytes = x25519.X25519PrivateKey.from_private_bytes(secret_key)
    public_key_bytes = x25519.X25519PublicKey.from_public_bytes(public_key)

    shared_secret_raw = secret_key_bytes.exchange(public_key_bytes)

    return shared_secret_raw

def generate_keypair() -> Tuple[x25519.X25519PrivateKey, x25519.X25519PublicKey]:
    secret_key_bytes = x25519.X25519PrivateKey.generate()
    public_key_bytes = secret_key_bytes.public_key()
    return secret_key_bytes, public_key_bytes