import os
import bcrypt
import base64
import hashlib

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes as _crypto_hashes


def HashPassword(password, rounds=12):
    if isinstance(password, str):
        password = password.encode('utf-8')
    return bcrypt.hashpw(password, bcrypt.gensalt(rounds=rounds))


def VerifyPassword(password, hashed):
    if isinstance(password, str):
        password = password.encode('utf-8')
    if isinstance(hashed, str):
        hashed = hashed.encode('utf-8')
    return bcrypt.checkpw(password, hashed)


def _derive_key(master_secret):
    """使用 HKDF-SHA256 从任意长度的主密钥派生 256-bit AES 密钥"""
    if isinstance(master_secret, str):
        master_secret = master_secret.encode('utf-8')
    hkdf = HKDF(
        algorithm=_crypto_hashes.SHA256(),
        length=32,
        salt=b'gvsds-crypto-salt',
        info=b'aes-gcm-256',
    )
    return hkdf.derive(master_secret)


def SymmetricEncrypt(plaintext, master_secret):
    """使用 AES-256-GCM 加密

    输出格式: base64(nonce_12 + ciphertext + tag_16)
    """
    if isinstance(plaintext, str):
        plaintext = plaintext.encode('utf-8')
    key = _derive_key(master_secret)
    aesgcm = AESGCM(key)
    nonce = os.urandom(12)
    ciphertext_with_tag = aesgcm.encrypt(nonce, plaintext, None)
    return base64.b64encode(nonce + ciphertext_with_tag).decode('ascii')


def SymmetricDecrypt(ciphertext_b64, master_secret):
    """使用 AES-256-GCM 解密

    输入格式: base64(nonce_12 + ciphertext + tag_16)
    """
    raw = base64.b64decode(ciphertext_b64)
    nonce = raw[:12]
    ciphertext_with_tag = raw[12:]
    key = _derive_key(master_secret)
    aesgcm = AESGCM(key)
    plaintext = aesgcm.decrypt(nonce, ciphertext_with_tag, None)
    return plaintext.decode('utf-8')


def FileSHA1(filepath):
    sha1 = hashlib.sha1()
    with open(filepath, 'rb') as f:
        while True:
            chunk = f.read(8192)
            if not chunk:
                break
            sha1.update(chunk)
    return sha1.hexdigest()


def FileSHA256(filepath):
    sha256 = hashlib.sha256()
    with open(filepath, 'rb') as f:
        while True:
            chunk = f.read(8192)
            if not chunk:
                break
            sha256.update(chunk)
    return sha256.hexdigest()


def BytesSHA1(data):
    return hashlib.sha1(data).hexdigest()


def BytesSHA256(data):
    return hashlib.sha256(data).hexdigest()
