use super::Address;
use crate::log;

use thiserror::Error;

//use secp256k1::{
//    ecdsa::{RecoveryId, Signature},
//    Message, PublicKey, Secp256k1, SecretKey,
//};
//
//use ring::digest;
//

#[allow(dead_code)]
#[derive(Error, Debug)]
pub enum TxError {
  #[error("from field is empty")]
  FromEmpty,
  #[error("to field is empty")]
  ToEmpty,
  #[error("insuffitient fonds")]
  FromInsuffitientFonds,
  #[error("0 value transaction")]
  ValueEmpty,
  #[error("account {0} not found in database")]
  UnknownAccount(String),
}

pub fn print_error_chain(err: &anyhow::Error) {
  let mut err_string = String::from(format!("Error: {}\n", err));

  let mut source = err.source();
  let mut level = 1;

  while let Some(err) = source {
    err_string.push_str(format!("  {}: {}\n", level, err).as_str());
    source = err.source();
    level += 1;
  }
  log(err_string)
}

#[derive(
  serde::Deserialize, serde::Serialize, Debug, clap::Args, Clone, bincode::Encode, bincode::Decode,
)]
pub struct Tx {
  from: Address,
  to: Address,
  value: u64,
  data: String,
  nonce: u64,
  signature: Vec<u8>,
}

impl Tx {
  pub fn new(from: Address, to: Address, value: u64, nonce: u64, data: String) -> Self {
    Self {
      from,
      to,
      value,
      data,
      nonce,
      signature: vec![],
    }
  }

  pub fn validate(&self) -> Result<(), TxError> {
    if self.from.is_empty() {
      return Err(TxError::FromEmpty);
    } else if self.to.is_empty() {
      return Err(TxError::ToEmpty);
    } else if self.value == 0 {
      return Err(TxError::ValueEmpty);
    }
    //let secp = secp256k1::Secp256k1::new();
    //let mut tx_hash = [0u8; 32];
    //tx_hash.copy_from_slice(self.hash().as_bytes());
    //let message = secp256k1::Message::from_digest(tx_hash);
    //let signature = secp256k1::ecdsa::Signature::from_compact(&self.signature).unwrap();
    Ok(())
  }

  pub fn hash(&self) -> String {
    super::Hasher::hash_chain_data(&super::ChainData::Transaction(self.clone()))
  }

  pub fn from(&self) -> &Address {
    &self.from
  }
  pub fn to(&self) -> &Address {
    &self.to
  }
  pub fn value(&self) -> u64 {
    self.value
  }
  pub fn data(&self) -> &str {
    &self.data
  }
}
