# -*- coding: utf-8 -*-
# Copyright 2018 ICON Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from base64 import b64encode
from hashlib import sha3_256

from iconsdk.builder.transaction_builder import Transaction
from iconsdk.exception import DataTypeException
from iconsdk.libs.serializer import serialize
from iconsdk.utils import get_timestamp
from iconsdk.utils.convert_type import convert_int_to_hex_str
from iconsdk.utils.gen_tx_data import generate_data_value
from iconsdk.wallet.wallet import Wallet


class SignedTransaction:

    def __init__(self, transaction: Transaction, wallet: Wallet, step_limit: int = None):
        """Converts raw transaction into the signed transaction object having a signature.

        :param transaction: A transaction object not having a signature field yet
        :param wallet: A wallet object
        """
        if step_limit is not None:
            transaction.step_limit = step_limit
        if transaction.step_limit is None:
            raise DataTypeException("Transaction should have step limit when signed.")

        self.__signed_transaction_dict = self.convert_tx_to_jsonrpc_request(transaction, wallet)
        message_hash = sha3_256(serialize(self.__signed_transaction_dict)).digest()
        signature = wallet.sign(message_hash)
        self.__signed_transaction_dict["signature"] = b64encode(signature).decode()

    @property
    def signed_transaction_dict(self) -> dict:
        return self.__signed_transaction_dict

    @staticmethod
    def convert_tx_to_jsonrpc_request(transaction: Transaction, wallet: Wallet = None) -> dict:
        """Converts an instance of the transaction into JSON RPC request in dict"""
        dict_tx = {
            "version": convert_int_to_hex_str(transaction.version) if transaction.version else "0x3",
            "from": transaction.from_ if transaction.from_ else wallet.get_address(),
            "to": transaction.to,
            "stepLimit": convert_int_to_hex_str(transaction.step_limit),
            "timestamp": convert_int_to_hex_str(transaction.timestamp) if transaction.timestamp else get_timestamp(),
            "nid": convert_int_to_hex_str(transaction.nid) if transaction.nid else "0x1"
        }

        if transaction.value is not None:
            dict_tx["value"] = convert_int_to_hex_str(transaction.value)

        if transaction.nonce is not None:
            dict_tx["nonce"] = convert_int_to_hex_str(transaction.nonce)

        if transaction.data_type is not None:
            dict_tx["dataType"] = transaction.data_type

        if transaction.data_type in ('deploy', 'call'):
            dict_tx["data"] = generate_data_value(transaction)
        elif transaction.data_type == 'message':
            dict_tx["data"] = transaction.data
        elif transaction.data_type == "deposit":
            dict_tx["data"] = {"action": transaction.action}
            if transaction.action == "withdraw":
                if transaction.id is not None:
                    dict_tx["data"]["id"] = transaction.id
                elif transaction.amount is not None:
                    dict_tx["data"]["amount"] = convert_int_to_hex_str(transaction.amount)

        return dict_tx
