# -*- coding: utf-8 -*-
"""
AES-256-CBC 加密器
用于文件名加密
"""

import os
from typing import Optional

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes, padding
from cryptography.hazmat.backends import default_backend

from .base import CryptoBase, CryptoError


class AESEncryptor(CryptoBase):
    """
    AES-256-CBC 加密器

    使用 PBKDF2-HMAC-SHA1 派生密钥，迭代 1,000 次。
    使用 AES-256-CBC 模式加密，PKCS7 填充。

    常量:
        SALT_SIZE: 盐值大小（8 字节）
        KEY_SIZE: 密钥大小（32 字节）
        IV_SIZE: IV 大小（16 字节）
        PBKDF2_ITERATIONS: PBKDF2 迭代次数（1,000）
        DERIVED_KEY_SIZE: 派生密钥总大小（48 字节 = 32 字节密钥 + 16 字节 IV）
    """

    SALT_SIZE: int = 8
    KEY_SIZE: int = 32
    IV_SIZE: int = 16
    PBKDF2_ITERATIONS: int = 1_000
    DERIVED_KEY_SIZE: int = 48

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

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

        Raises:
            ValueError: 密码为空或 salt 长度不正确
        """
        if not password:
            raise ValueError("密码不能为空")

        if salt is None:
            salt = os.urandom(self.SALT_SIZE)
        elif len(salt) != self.SALT_SIZE:
            raise ValueError(f"salt 长度必须为 {self.SALT_SIZE} 字节")

        super().__init__(password, salt)
        self._iv: Optional[bytes] = None
        self._cipher: Optional[Cipher] = None

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

        使用 PBKDF2-HMAC-SHA1 派生 48 字节数据：
        - 前 32 字节：AES-256 密钥
        - 后 16 字节：IV

        Raises:
            CryptoError: 密钥派生失败
        """
        try:
            kdf = PBKDF2HMAC(
                algorithm=hashes.SHA1(),
                length=self.DERIVED_KEY_SIZE,
                salt=self.salt,
                iterations=self.PBKDF2_ITERATIONS,
                backend=default_backend(),
            )
            derived = kdf.derive(self.password.encode())

            self._key = derived[:self.KEY_SIZE]
            self._iv = derived[self.KEY_SIZE:self.DERIVED_KEY_SIZE]

            # 初始化加密器
            cipher = Cipher(
                algorithms.AES(self._key),
                modes.CBC(self._iv),
                backend=default_backend(),
            )
            self._cipher = cipher.encryptor()

        except Exception as e:
            raise CryptoError(f"密钥派生失败: {e}") from e

    async def encrypt(self, data: bytes, offset: int = 0) -> bytes:
        """
        加密数据

        使用 AES-256-CBC 加密数据，自动添加 PKCS7 填充。

        Args:
            data: 要加密的数据
            offset: 数据偏移（AES-CBC 不使用，保留参数以兼容接口）

        Returns:
            bytes: 加密后的数据（包含填充）

        Raises:
            CryptoError: 加密失败
            ValueError: 数据为空

        Example:
            >>> encryptor = AESEncryptor("password")
            >>> await encryptor.derive_key()
            >>> encrypted = await encryptor.encrypt(b"Hello, World!")
            >>> len(encrypted)
            16  # 13 字节数据 + 3 字节填充
        """
        if not data:
            raise ValueError("数据不能为空")

        self._ensure_key_derived()

        if self._cipher is None:
            raise CryptoError("加密器未初始化")

        try:
            # 添加 PKCS7 填充
            padder = padding.PKCS7(128).padder()
            padded_data = padder.update(data) + padder.finalize()

            # 加密数据
            encrypted = self._cipher.update(padded_data) + self._cipher.finalize()

            return encrypted

        except Exception as e:
            raise CryptoError(f"加密失败: {e}") from e

    @property
    def iv(self) -> bytes:
        """
        获取 IV

        Returns:
            bytes: IV（16 字节）

        Raises:
            CryptoError: IV 未初始化
        """
        if self._iv is None:
            raise CryptoError("IV 未初始化")
        return self._iv

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

        Returns:
            bytes: 密钥（32 字节）

        Raises:
            CryptoError: 密钥未派生
        """
        self._ensure_key_derived()
        return self._key
