use std::convert::TryFrom;
use std::num::ParseIntError;
use std::str::FromStr;

use ergo_chain_types::Base16DecodedBytes;
use ergotree_ir::sigma_protocol::sigma_boolean::SigmaBoolean;
use serde::Deserialize;
use serde::Serialize;

use crate::sigma_protocol::challenge::Challenge;
use crate::sigma_protocol::prover::hint::RealSecretProof;
use crate::sigma_protocol::prover::hint::SimulatedSecretProof;
use crate::sigma_protocol::sig_serializer::parse_sig_compute_challenges;
use crate::sigma_protocol::sig_serializer::SigParsingError;
use crate::sigma_protocol::unproven_tree::NodePosition;

#[derive(Deserialize, Serialize)]
pub struct NodePositionJson(String);

impl From<NodePosition> for NodePositionJson {
    fn from(p: NodePosition) -> Self {
        NodePositionJson(
            p.positions
                .into_iter()
                .map(|p| p.to_string())
                .collect::<Vec<String>>()
                .join("-"),
        )
    }
}

impl TryFrom<NodePositionJson> for NodePosition {
    type Error = ParseIntError;

    fn try_from(pj: NodePositionJson) -> Result<Self, Self::Error> {
        Ok(NodePosition {
            positions: pj
                .0
                .split('-')
                .map(usize::from_str)
                .collect::<Result<Vec<usize>, _>>()?,
        })
    }
}

#[derive(Deserialize)]
pub struct RealSecretProofJson {
    #[serde(rename = "pubkey")]
    pub image: SigmaBoolean,
    #[serde(rename = "challenge")]
    pub challenge: Challenge,
    #[serde(rename = "proof")]
    pub unchecked_tree_bytes: Base16DecodedBytes,
    #[serde(rename = "position")]
    pub position: NodePosition,
}

impl TryFrom<RealSecretProofJson> for RealSecretProof {
    type Error = SigParsingError;

    fn try_from(rj: RealSecretProofJson) -> Result<Self, Self::Error> {
        let unchecked_tree =
            parse_sig_compute_challenges(&rj.image, rj.unchecked_tree_bytes.into())?;
        Ok(RealSecretProof {
            image: rj.image,
            challenge: rj.challenge,
            unchecked_tree,
            position: rj.position,
        })
    }
}

#[derive(Deserialize)]
pub struct SimulatedSecretProofJson {
    #[serde(rename = "pubkey")]
    pub image: SigmaBoolean,
    #[serde(rename = "challenge")]
    pub challenge: Challenge,
    #[serde(rename = "proof")]
    pub unchecked_tree_bytes: Base16DecodedBytes,
    #[serde(rename = "position")]
    pub position: NodePosition,
}

impl TryFrom<SimulatedSecretProofJson> for SimulatedSecretProof {
    type Error = SigParsingError;

    fn try_from(sj: SimulatedSecretProofJson) -> Result<Self, Self::Error> {
        let unchecked_tree =
            parse_sig_compute_challenges(&sj.image, sj.unchecked_tree_bytes.into())?;
        Ok(SimulatedSecretProof {
            image: sj.image,
            challenge: sj.challenge,
            unchecked_tree,
            position: sj.position,
        })
    }
}
