"""
==============================================================================
ED25519 消息签名模块 (message.py)
==============================================================================

本模块实现基于 AE 账户的消息签名和验证功能。
与 aepp-sdk-js 完全兼容，支持 Superhero Wallet 消息签名。

==============================================================================
技术原理
==============================================================================

1. aepp-sdk-js 实现对照:
   - hashMessage(message)  -> 对消息进行特殊格式的 blake2b hash
   - signMessage(message, privateKey) -> 对 hash 进行 ED25519 签名
   - verifyMessage(message, signature, publicKey) -> 验证签名

2. 消息格式 (AE 标准):
   - 前缀: "aeknow Signed Message:\n" (26 字节)
   - 格式: blake2b([prefix_len, prefix, msg_len, msg])
   - 类似以太坊的 "\x19Ethereum Signed Message:\n" 前缀

3. JS SDK 源码对照 (crypto.ts):
   ```javascript
   // hashMessage 函数
   const p = Buffer.from('aeknow Signed Message:\n', 'utf8');
   const msgBuffer = typeof message === 'string' ? Buffer.from(message, 'utf8') : message;
   return blake2b(Buffer.concat([toBytes(p.length), p, toBytes(msgBuffer.length), msgBuffer]));
   ```

4. 签名算法: ED25519 (NaCl/libsodium)
   - 私钥: 32 字节
   - 公钥: 32 字节  
   - 签名: 64 字节 (128 字符十六进制字符串)

5. 使用示例:
   ```python
   from aeknow.message import sign_message, verify_message
   from aeknow.signing import Account
   
   # 签名消息
   account = Account.from_keystore("wallet.json", "password")
   signature = sign_message(account, "Hello aeknow!")
   
   # 验证签名
   is_valid = verify_message(account.get_address(), "Hello aeknow!", signature)
   ```

==============================================================================
"""

from nacl.encoding import RawEncoder
from nacl.hash import blake2b

from aeknow import signing

# =============================================================================
# AE SDK 消息签名前缀
# =============================================================================
# 对应 JS SDK: const p = Buffer.from('aeknow Signed Message:\n', 'utf8');
# 前缀长度: 26 字节
# =============================================================================
AE_MESSAGE_PREFIX = b'aeknow Signed Message:\n'


def hash_message(message: str) -> bytes:
    """
    按 AE SDK 标准格式对消息进行 hash
    
    ==========================================================================
    aepp-sdk-js 对应实现 (crypto.ts - hashMessage):
    ==========================================================================
    ```javascript
    export function hashMessage(message: string | Uint8Array): Uint8Array {
      const p = Buffer.from('aeknow Signed Message:\n', 'utf8');
      const msgBuffer = typeof message === 'string' 
        ? Buffer.from(message, 'utf8') 
        : message;
      return blake2b(
        Buffer.concat([
          toBytes(p.length),           // 前缀长度 (1 字节)
          p,                           // 前缀内容 (26 字节)
          toBytes(msgBuffer.length),   // 消息长度 (1 字节)
          msgBuffer                    // 消息内容
        ])
      );
    }
    ```
    ==========================================================================
    
    :param message: 消息字符串 (UTF-8 编码)
    :return: 32 字节的 blake2b hash
    
    示例:
        message = "Hello"
        拼接后: [26] + "aeknow Signed Message:\n" + [5] + "Hello"
        返回: blake2b(data, digest_size=32)
    """
    # Step 1: 将消息转为 UTF-8 字节
    # 对应 JS: Buffer.from(message, 'utf8')
    msg_bytes = message.encode('utf-8')
    
    # Step 2: 拼接数据
    # 格式: [prefix_len] + prefix + [msg_len] + msg
    # 对应 JS: Buffer.concat([toBytes(p.length), p, toBytes(msgBuffer.length), msgBuffer])
    data = (
        bytes([len(AE_MESSAGE_PREFIX)]) +  # 前缀长度: 26 -> 0x1a
        AE_MESSAGE_PREFIX +                 # 前缀: "aeknow Signed Message:\n"
        bytes([len(msg_bytes)]) +           # 消息长度
        msg_bytes                           # 消息内容
    )
    
    # Step 3: 计算 blake2b hash (32 字节)
    # 对应 JS: blake2b(buffer) - 默认 32 字节输出
    return blake2b(data, digest_size=32, encoder=RawEncoder)


def sign_message(account: signing.Account, message: str) -> str:
    """
    使用 AE 账户对消息进行签名
    
    :param account: AE Account 对象
    :param message: 要签名的消息
    :return: 签名的十六进制字符串 (128 字符)
    
    流程:
        1. hash_message(message) -> 32 字节 hash
        2. account.sign(hash) -> 64 字节签名
        3. signature.hex() -> 128 字符十六进制
    """
    msg_hash = hash_message(message)
    signature = account.sign(msg_hash)
    return signature.hex()


def verify_message(address: str, message: str, signature: str) -> bool:
    """
    验证消息签名
    
    :param address: 签名者的 AE 地址 (ak_xxx)
    :param message: 原始消息
    :param signature: 签名的十六进制字符串
    :return: 签名是否有效
    
    流程:
        1. hash_message(message) -> 32 字节 hash
        2. bytes.fromhex(signature) -> 64 字节签名
        3. is_signature_valid(address, signature, hash) -> bool
    """
    try:
        msg_hash = hash_message(message)
        sig_bytes = bytes.fromhex(signature)
        return signing.is_signature_valid(address, sig_bytes, msg_hash)
    except Exception:
        return False


class MessageSigner:
    """
    消息签名工具类
    
    封装了账户管理和消息签名/验证操作
    """
    
    def __init__(self, account: signing.Account = None):
        self.account = account
    
    @classmethod
    def from_keystore(cls, path: str, password: str) -> 'MessageSigner':
        """从 Keystore 加载账户"""
        account = signing.Account.from_keystore(path, password)
        return cls(account)
    
    @classmethod
    def from_mnemonic(cls, mnemonic: str) -> 'MessageSigner':
        """
        从 BIP39 助记词加载账户
        
        使用标准派生路径 m/44'/457'/0'/0'/0' 生成主账户
        
        :param mnemonic: BIP39 助记词 (12 或 24 个单词, 空格分隔)
        :return: MessageSigner 实例
        
        对应 JS SDK:
          const account = Account.fromMnemonic(mnemonic);
        """
        from aeknow.hdwallet import HDWallet
        wallet = HDWallet(mnemonic)
        account = wallet.get_master_account()
        return cls(account)
    
    @classmethod
    def from_secret_key(cls, secret_key: str) -> 'MessageSigner':
        """从私钥加载账户"""
        account = signing.Account.from_secret_key_string(secret_key)
        return cls(account)
    
    @classmethod
    def generate(cls) -> 'MessageSigner':
        """生成新账户"""
        account = signing.Account.generate()
        return cls(account)
    
    @property
    def address(self) -> str:
        """获取账户地址"""
        return self.account.get_address() if self.account else None
    
    def sign(self, message: str) -> dict:
        """
        对消息进行签名
        
        :param message: 要签名的消息
        :return: 包含 message, address, signature, message_hash 的字典
        """
        if not self.account:
            raise ValueError("未加载账户")
        
        msg_hash = hash_message(message)
        signature = self.account.sign(msg_hash)
        
        return {
            'message': message,
            'address': self.account.get_address(),
            'signature': signature.hex(),
            'message_hash': msg_hash.hex()
        }
    
    def verify(self, address: str, message: str, signature: str) -> bool:
        """
        验证消息签名
        
        :param address: 签名者地址
        :param message: 原始消息
        :param signature: 签名的十六进制字符串
        :return: 签名是否有效
        """
        return verify_message(address, message, signature)


def print_signature_info(signature_result: dict):
    """
    打印签名信息
    
    :param signature_result: MessageSigner.sign() 返回的签名结果字典
    """
    print("\n" + "="*70)
    print("消息签名信息")
    print("="*70)
    print(f"签名者: {signature_result['address']}")
    print(f"消息:   {signature_result['message']}")
    print(f"签名:   {signature_result['signature']}")
    print(f"Hash:   {signature_result['message_hash']}")
    print("="*70 + "\n")
