use std::fmt::Debug;

use elements::hex::ToHex;
use lwk_common::Network;
use serde::{ser::SerializeStruct, Deserialize, Serialize};

use crate::get_receive_address::SingleOrMulti;

#[derive(Serialize)]
pub struct SignLiquidTxParams {
    pub network: Network,

    #[serde(with = "serde_bytes")]
    pub txn: Vec<u8>,

    pub num_inputs: u32,

    pub use_ae_signatures: bool,

    pub change: Vec<Option<Change>>,

    pub asset_info: Vec<AssetInfo>,

    pub trusted_commitments: Vec<Option<Commitment>>,

    pub additional_info: Option<AdditionalInfo>,
}

impl std::fmt::Debug for SignLiquidTxParams {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("SignLiquidTxParams")
            .field("network", &self.network)
            .field("txn_bytes", &self.txn.len())
            .field("num_inputs", &self.num_inputs)
            .field("use_ae_signatures", &self.use_ae_signatures)
            .field("change", &self.change)
            .field("asset_info", &self.asset_info)
            .field("trusted_commitments", &self.trusted_commitments)
            .field("additional_info", &self.additional_info)
            .finish()
    }
}

#[derive(Deserialize, Serialize)]
pub struct Commitment {
    #[serde(with = "serde_bytes")]
    pub asset_generator: Vec<u8>,

    #[serde(with = "serde_bytes")]
    pub asset_id: Vec<u8>,

    #[serde(with = "serde_bytes")]
    pub blinding_key: Vec<u8>,

    pub value: u64,

    #[serde(with = "serde_bytes")]
    pub value_commitment: Vec<u8>,

    #[serde(with = "serde_bytes")]
    pub value_blind_proof: Vec<u8>,

    #[serde(with = "serde_bytes")]
    pub asset_blind_proof: Vec<u8>,
}

impl std::fmt::Debug for Commitment {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Commitment")
            .field("asset_generator", &self.asset_generator.to_hex())
            .field("asset_id", &self.asset_id.to_hex())
            .field("blinding_key", &self.blinding_key.to_hex())
            .field("value", &self.value)
            .field("value_commitment", &self.value_commitment.to_hex())
            .field("value_blind_proof", &self.value_blind_proof.to_hex())
            .field("asset_blind_proof", &self.asset_blind_proof.to_hex())
            .finish()
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct Change {
    pub address: SingleOrMulti,
    pub is_change: bool,
}

impl Serialize for Change {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let mut state = serializer.serialize_struct("Change", 2)?;
        match &self.address {
            SingleOrMulti::Single { variant, path } => {
                state.serialize_field("variant", variant)?;
                state.serialize_field("path", path)?;
            }
            SingleOrMulti::Multi {
                multisig_name,
                paths,
            } => {
                state.serialize_field("multisig_name", multisig_name)?;
                state.serialize_field("paths", paths)?;
            }
        }

        state.end()
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct AssetInfo {
    pub asset_id: String,
    pub contract: Contract,
    pub issuance_prevout: Prevout,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Contract {
    pub entity: Entity,

    pub issuer_pubkey: String,
    pub name: String,
    pub precision: u8,
    pub ticker: String,
    pub version: u8,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct AdditionalInfo {
    pub tx_type: String,
    pub wallet_input_summary: Vec<Summary>,
    pub wallet_output_summary: Vec<Summary>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Summary {
    #[serde(with = "serde_bytes")]
    pub asset_id: Vec<u8>,
    pub satoshi: u64,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Prevout {
    pub txid: String,
    pub vout: u32,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Entity {
    pub domain: String,
}

#[derive(Deserialize, Serialize)]
pub struct TxInputParams {
    pub is_witness: bool,

    #[serde(with = "serde_bytes", rename = "script")]
    pub script_code: Vec<u8>,

    #[serde(with = "serde_bytes")]
    pub value_commitment: Vec<u8>,

    pub path: Vec<u32>,

    pub sighash: Option<u32>,

    /// 32 bytes anti-exfiltration commitment (random data not verified for now). TODO verify
    #[serde(with = "serde_bytes")]
    pub ae_host_commitment: Vec<u8>,
}

impl Debug for TxInputParams {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TxInputParams")
            .field("is_witness", &self.is_witness)
            .field("script_code", &self.script_code.to_hex())
            .field("value_commitment", &self.value_commitment.to_hex())
            .field("path", &self.path)
            .field("sighash", &self.sighash)
            .field("ae_host_commitment", &self.ae_host_commitment.to_hex())
            .finish()
    }
}

#[cfg(test)]
mod test {

    use serde_json::Value;

    use crate::get_receive_address::{SingleOrMulti, Variant};

    use super::Change;

    #[test]
    fn parse_change() {
        let json = include_str!("../test_data/sign_liquid_tx_request.json");

        let resp: Value = serde_json::from_str(json).unwrap();

        let params = resp.get("params").unwrap();
        let changes = params.get("change").unwrap();
        let change = changes.get(1).unwrap();

        let expected = Change {
            address: SingleOrMulti::Single {
                variant: Variant::ShWpkh,
                path: vec![2147483697, 2147483648, 2147483648, 0, 143],
            },
            is_change: false,
        };

        assert_eq!(&serde_json::to_value(expected).unwrap(), change);
    }
}
