use std::str::FromStr;
use rust_util::XResult;
use secp256k1::{Message, PublicKey, Secp256k1, SecretKey, Signature};
use serde::{Serialize, Deserialize};
use crate::util::{calc_sha256, make_btc_address};

#[derive(Debug, Serialize, Deserialize)]
pub struct Transaction {
    pub body: String,
    pub sender: String,
    pub public_key: String,
    pub signature: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct TransactionBody {
    pub timestamp: u64,
    pub nonce: String,
    pub contract: String,
    pub action: String,
    pub parameters: String,
}

impl Transaction {
    pub fn new(transaction_body: &TransactionBody, priv_key: &SecretKey, pub_key: &PublicKey) -> XResult<Self> {
        let body = serde_json::to_string(transaction_body)?;
        let sender = make_btc_address(&pub_key);
        let public_key = format!("{}", pub_key);
        
        let message = get_body_message(&body)?;
        let sign = Secp256k1::new().sign(&message, priv_key);

        Ok(Self{
            body,
            sender,
            public_key,
            signature: format!("{}", sign),
        })
    }
    
    pub fn parse_body(&self) -> XResult<TransactionBody> {
        serde_json::from_str(&self.body).map_err(|e| e.into())
    }

    pub fn verify(&self) -> XResult<()> {
        let public_key = PublicKey::from_str(&self.public_key)?;
        let address = make_btc_address(&public_key);
        if &address != &self.sender {
            return simple_error!("Assress and sender not match!");
        }
        let message = get_body_message(&self.body)?;
        let sig = Signature::from_str(&self.signature)?;
        Secp256k1::new().verify(&message, &sig, &public_key).map_err(|e| e.into())
    }
}

fn get_body_message(body: &str) -> XResult<Message> {
    let digest= calc_sha256(body.as_bytes());
    Message::from_slice(&digest).map_err(|e| e.into())
}
