# -*- coding: utf-8 -*-
"""
ChaCha20 加密器

XChaCha20-Poly1305 加密器，使用 pycryptodome 实现。
与 CloudDrive 完全兼容，使用 24 字节 nonce（XChaCha20 格式）。
"""

from Crypto.Cipher import ChaCha20_Poly1305
from Crypto.Random import get_random_bytes
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
from typing import Optional


class ChaCha20Encryptor:
    """
    ChaCha20 加密器

    XChaCha20-Poly1305 加密器，使用完整的 24 字节 nonce。
    与 CloudDrive (CD2) 完全兼容。

    常量:
        SALT_SIZE: 盐值大小（16 字节）
        NONCE_SIZE: Nonce 大小（24 字节）
        KEY_SIZE: 密钥大小（32 字节）
        PBKDF2_ITERATIONS: PBKDF2 迭代次数（100,000）
    """

    SALT_SIZE: int = 16
    NONCE_SIZE: int = 24  # 24 字节 nonce（XChaCha20）
    KEY_SIZE: int = 32
    PBKDF2_ITERATIONS: int = 100_000  # 生产环境推荐值

    def __init__(self, password: str, salt: Optional[bytes] = None) -> None:
        """
        初始化 ChaCha20 加密器

        Args:
            password: 加密密码
            salt: 盐值（16 字节），如果为 None 则随机生成

        Raises:
            ValueError: 参数错误
        """
        if salt is None:
            salt = get_random_bytes(self.SALT_SIZE)
        elif len(salt) != self.SALT_SIZE:
            raise ValueError(f"salt 长度必须为 {self.SALT_SIZE} 字节，当前：{len(salt)}")

        self.password = password
        self.salt = salt
        self._key: Optional[bytes] = None

    @property
    def key(self) -> bytes:
        """
        获取派生的密钥

        Returns:
            bytes: 32 字节的密钥

        Raises:
            ValueError: 密钥未派生
        """
        if self._key is None:
            raise ValueError("密钥未派生，请先调用 derive_key()")
        return self._key

    def derive_key(self) -> None:
        """
        派生密钥

        使用 PBKDF2-HMAC-SHA256 从密码派生密钥。

        Raises:
            ValueError: 派生失败
        """
        try:
            self._key = PBKDF2(
                self.password.encode(),
                self.salt,
                dkLen=self.KEY_SIZE,
                count=self.PBKDF2_ITERATIONS,
                hmac_hash_module=SHA256
            )
        except Exception as e:
            raise ValueError(f"密钥派生失败：{e}") from e

    def encrypt(self, data: bytes, nonce: bytes) -> bytes:
        """
        加密数据

        Args:
            data: 原始数据
            nonce: Nonce（24 字节）

        Returns:
            bytes: 加密后的数据（包含 16 字节 Poly1305 tag）

        Raises:
            ValueError: 加密失败
        """
        if self._key is None:
            self.derive_key()

        if len(nonce) != self.NONCE_SIZE:
            raise ValueError(f"nonce 长度必须为 {self.NONCE_SIZE} 字节，当前：{len(nonce)}")

        try:
            # 使用 XChaCha20Poly1305，需要完整的 24 字节 nonce
            cipher = ChaCha20_Poly1305.new(key=self._key, nonce=nonce)
            ciphertext, tag = cipher.encrypt_and_digest(data)
            # 返回密文 + tag（与 CD2 格式一致）
            return ciphertext + tag
        except Exception as e:
            raise ValueError(f"加密失败：{e}") from e

    def decrypt(self, ciphertext: bytes, nonce: bytes) -> bytes:
        """
        解密数据

        Args:
            ciphertext: 加密数据（包含 16 字节 Poly1305 tag）
            nonce: Nonce（24 字节）

        Returns:
            bytes: 解密后的原始数据

        Raises:
            ValueError: 解密失败
        """
        if self._key is None:
            self.derive_key()

        if len(nonce) != self.NONCE_SIZE:
            raise ValueError(f"nonce 长度必须为 {self.NONCE_SIZE} 字节，当前：{len(nonce)}")

        if len(ciphertext) < 16:
            raise ValueError("密文长度不足，无法包含 Poly1305 tag")

        try:
            # 分离密文和 tag
            actual_ciphertext = ciphertext[:-16]
            tag = ciphertext[-16:]

            # 使用 XChaCha20Poly1305 解密
            cipher = ChaCha20_Poly1305.new(key=self._key, nonce=nonce)
            return cipher.decrypt_and_verify(actual_ciphertext, tag)
        except Exception as e:
            raise ValueError(f"解密失败：{e}") from e
