import logging
"""
==============================================================================
预言机模块 (oracles.py)
==============================================================================

本模块实现 aeknow 预言机功能。
支持预言机注册、查询、响应和延期操作。

==============================================================================
使用示例
==============================================================================

1. 注册预言机:
    ```python
    from aeknow.node import NodeClient
    from aeknow.signing import Account
    
    client = NodeClient(NodeClient.Config(
        external_url="https://testnet.aeknow.io",
        network_id="ae_uat"
    ))
    account = Account.from_keystore("wallet.json", "password")
    
    # 创建预言机实例
    oracle = client.Oracle()
    
    # 注册预言机
    register_tx = oracle.register(
        account=account,
        query_format="string",  # 查询格式
        response_format="string",  # 响应格式
        query_fee=1000000000000000,  # 查询费用
        ttl_value=500  # 预言机有效期（区块数）
    )
    print(f"预言机 ID: {oracle.id}")
    print(f"注册 TX: {register_tx.hash}")
    ```

2. 查询预言机:
    ```python
    # 创建查询对象
    oracle_query = client.OracleQuery(
        oracle_id="ok_xxxxx..."  # 预言机 ID
    )
    
    # 发送查询
    query_tx = oracle_query.query(
        sender=account,
        query="What is the price of AE?",
        query_fee=1000000000000000,  # 支付给预言机的费用
        query_ttl_value=50,  # 查询有效期
        response_ttl_value=50  # 响应有效期
    )
    print(f"查询 TX: {query_tx.hash}")
    print(f"查询 ID: {oracle_query.id}")
    ```

3. 预言机响应查询:
    ```python
    # 预言机账户响应查询
    respond_tx = oracle.respond(
        account=account,
        query_id="oq_xxxxx...",  # 查询 ID
        response="The price is $5.00",
        response_ttl_value=50
    )
    print(f"响应 TX: {respond_tx.hash}")
    ```

4. 获取预言机响应:
    ```python
    # 查询响应结果
    response_obj = oracle_query.get_response_object()
    print(f"响应内容: {response_obj.response}")
    print(f"响应时间: {response_obj.response_ttl}")
    ```

5. 延长预言机有效期:
    ```python
    # 延长预言机 TTL
    extend_tx = oracle.extend(
        account=account,
        query_id="oq_xxxxx...",
        response="",
        ttl_value=100  # 延长 100 个区块
    )
    print(f"延期 TX: {extend_tx.hash}")
    ```

6. 完整的预言机工作流:
    ```python
    # 1. 预言机提供者注册
    oracle_account = Account.from_keystore("oracle_wallet.json", "password")
    oracle = client.Oracle()
    
    register_tx = oracle.register(
        account=oracle_account,
        query_format="string",
        response_format="json",
        query_fee=1000000000000000
    )
    client.wait_for_confirmation(register_tx.hash)
    
    # 2. 用户发送查询
    user_account = Account.from_keystore("user_wallet.json", "password")
    query = client.OracleQuery(oracle_id=oracle.id)
    
    query_tx = query.query(
        sender=user_account,
        query="Get weather data",
        query_fee=1000000000000000
    )
    client.wait_for_confirmation(query_tx.hash)
    
    # 3. 预言机响应
    respond_tx = oracle.respond(
        account=oracle_account,
        query_id=query.id,
        response='{"temperature": 25, "condition": "sunny"}'
    )
    client.wait_for_confirmation(respond_tx.hash)
    
    # 4. 用户获取结果
    response = query.get_response_object()
    print(f"预言机响应: {response.response}")
    ```

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

from aeknow import hashing, defaults
from aeknow.identifiers import ORACLE_ID

logger = logging.getLogger(__name__)


class OracleQuery():
    """
    Create and execute a oracle query
    """

    def __init__(self, client, oracle_id, id=None):
        self.oracle_id = oracle_id
        self.id = id
        self.client = client

    def execute(self, sender, query,
                query_fee=defaults.ORACLE_QUERY_FEE,
                query_ttl_type=defaults.ORACLE_TTL_TYPE,
                query_ttl_value=defaults.ORACLE_QUERY_TTL_VALUE,
                response_ttl_type=defaults.ORACLE_TTL_TYPE,
                response_ttl_value=defaults.ORACLE_RESPONSE_TTL_VALUE,
                fee=defaults.FEE,
                tx_ttl=defaults.TX_TTL):
        """
        Execute a query to the oracle
        """
        if self.oracle_id is None:
            raise ValueError("Oracle id must be provided before executing a query")
        # get the transaction builder
        txb = self.client.tx_builder
        # get the account nonce and ttl
        nonce, ttl = self.client._get_nonce_ttl(sender.get_address(), tx_ttl)
        # create spend_tx
        tx = txb.tx_oracle_query(self.oracle_id, sender.get_address(), query,
                                 query_fee, query_ttl_type, query_ttl_value,
                                 response_ttl_type, response_ttl_value,
                                 fee, ttl, nonce
                                 )
        # sign the transaction
        tx_signed = self.client.sign_transaction(sender, tx)
        # post the transaction to the chain
        self.client.broadcast_transaction(tx_signed)
        # save the query id
        self.id = hashing.oracle_query_id(sender.get_address(), nonce, self.oracle_id)
        # return the transaction
        return tx_signed

    def get_response_object(self):
        # TODO: workaround for dashes in the parameter names
        return self.client.get_oracle_query_by_pubkey_and_query_id(**{"pubkey": self.oracle_id, "query-id": self.id})


class Oracle():
    """

    """

    def __init__(self, client, oracle_id=None):
        self.client = client
        self.id = oracle_id
        self.query_id = None

    def register(self, account, query_format, response_format,
                 query_fee=defaults.ORACLE_QUERY_FEE,
                 ttl_type=defaults.ORACLE_TTL_TYPE,
                 ttl_value=defaults.ORACLE_TTL_VALUE,
                 vm_version=defaults.ORACLE_VM_VERSION,
                 fee=defaults.FEE,
                 tx_ttl=defaults.TX_TTL):
        """
        Execute a registration of an oracle
        """
        # get the transaction builder
        txb = self.client.tx_builder
        # get the account nonce and ttl
        nonce, ttl = self.client._get_nonce_ttl(account.get_address(), tx_ttl)
        # create spend_tx
        tx = txb.tx_oracle_register(
            account.get_address(),
            query_format, response_format,
            query_fee, ttl_type, ttl_value,
            vm_version, fee, ttl, nonce
        )
        # sign the transaction
        tx_signed = self.client.sign_transaction(account, tx)
        # post the transaction to the chain
        self.client.broadcast_transaction(tx_signed)
        # register the oracle id
        # the oracle id is the account that register the oracle
        # with the prefix substituted by with ok_
        self.id = f"{ORACLE_ID}_{account.get_address()[3:]}"
        # return the transaction
        return tx_signed

    def respond(self, account, query_id, response,
                response_ttl_type=defaults.ORACLE_TTL_TYPE,
                response_ttl_value=defaults.ORACLE_RESPONSE_TTL_VALUE,
                fee=defaults.FEE,
                tx_ttl=defaults.TX_TTL):
        """
        Post a response to an oracle query
        """
        if self.id is None:
            raise ValueError("Oracle id must be provided before respond to a query")
        # get the transaction builder
        txb = self.client.tx_builder
        # get the account nonce and ttl
        nonce, ttl = self.client._get_nonce_ttl(account.get_address(), tx_ttl)
        # create spend_tx
        tx = txb.tx_oracle_respond(self.id, query_id, response,
                                   response_ttl_type, response_ttl_value,
                                   fee, ttl, nonce
                                   )
        # sign the transaction
        tx_signed = self.client.sign_transaction(account, tx)
        # post the transaction to the chain
        self.client.broadcast_transaction(tx_signed)
        # return the transaction
        return tx_signed

    def extend(self, account, query_id, response,
               ttl_type=defaults.ORACLE_TTL_TYPE,
               ttl_value=defaults.ORACLE_TTL_VALUE,
               fee=defaults.FEE,
               tx_ttl=defaults.TX_TTL):
        """
        Extend the ttl of an oracle
        """
        if self.id is None:
            raise ValueError("Oracle id must be provided before extending")
        # get the transaction builder
        txb = self.client.tx_builder
        # get the account nonce and ttl
        nonce, ttl = self.client._get_nonce_ttl(account.get_address(), tx_ttl)
        # create spend_tx
        tx = txb.tx_oracle_extend(self.id, ttl_type, ttl_value, fee, ttl, nonce)
        # sign the transaction
        tx_signed = self.client.sign_transaction(account, tx)
        # post the transaction to the chain
        self.client.broadcast_transaction(tx_signed)
        # return the transaction
        return tx_signed
