use std::ops::Deref;
use std::{slice, vec};
use amplify::num::u7;
use bc::{
    ControlBlock, InternalPk, LeafScript, OutputPk, Parity, TapLeafHash, TapMerklePath,
    TapNodeHash, TapScript,
};
use commit_verify::mpc::MerkleBuoy;
use crate::{KeyOrigin, Terminal, XpubOrigin};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error, From)]
pub enum InvalidTree {
    #[from]
    #[display(doc_comments)]
    Unfinalized(UnfinalizedTree),
    #[from(FinalizedTree)]
    #[display("tap tree contains too many script leafs which doesn't fit a single Merkle tree")]
    MountainRange,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display("can't add more leafs to an already finalized tap tree")]
pub struct FinalizedTree;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display(
    "unfinalized tap tree containing leafs at level {0} which can't commit into a single Merkle \
     root"
)]
pub struct UnfinalizedTree(pub u7);
#[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct TapTreeBuilder {
    leafs: Vec<LeafInfo>,
    buoy: MerkleBuoy<u7>,
    finalized: bool,
}
impl TapTreeBuilder {
    pub fn new() -> Self { Self::default() }
    pub fn is_finalized(&self) -> bool { self.finalized }
    pub fn push_leaf(&mut self, leaf: LeafInfo) -> Result<bool, FinalizedTree> {
        if self.finalized {
            return Err(FinalizedTree);
        }
        let depth = leaf.depth;
        self.leafs.push(leaf);
        self.buoy.push(depth);
        if self.buoy.level() == u7::ZERO {
            self.finalized = true
        }
        Ok(self.finalized)
    }
    pub fn finish(self) -> Result<TapTree, UnfinalizedTree> {
        if !self.finalized {
            return Err(UnfinalizedTree(self.buoy.level()));
        }
        Ok(TapTree(self.leafs))
    }
}
#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(crate = "serde_crate", transparent))]
pub struct TapTree(Vec<LeafInfo>);
impl Deref for TapTree {
    type Target = Vec<LeafInfo>;
    fn deref(&self) -> &Self::Target { &self.0 }
}
impl IntoIterator for TapTree {
    type Item = LeafInfo;
    type IntoIter = vec::IntoIter<LeafInfo>;
    fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
impl<'a> IntoIterator for &'a TapTree {
    type Item = &'a LeafInfo;
    type IntoIter = slice::Iter<'a, LeafInfo>;
    fn into_iter(self) -> Self::IntoIter { self.0.iter() }
}
impl TapTree {
    pub fn from_leafs(leafs: impl IntoIterator<Item = LeafInfo>) -> Result<Self, InvalidTree> {
        let mut builder = TapTreeBuilder::new();
        for leaf in leafs {
            builder.push_leaf(leaf)?;
        }
        builder.finish().map_err(InvalidTree::from)
    }
    pub fn from_builder(builder: TapTreeBuilder) -> Result<Self, UnfinalizedTree> {
        builder.finish()
    }
    pub fn merkle_root(&self) -> TapNodeHash {
        todo!()
    }
    pub fn into_vec(self) -> Vec<LeafInfo> { self.0 }
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[cfg_attr(
    feature = "serde",
    derive(Serialize),
    serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct LeafInfo {
    pub depth: u7,
    pub script: LeafScript,
}
impl LeafInfo {
    pub fn tap_script(depth: u7, script: TapScript) -> Self {
        LeafInfo {
            depth,
            script: LeafScript::from_tap_script(script),
        }
    }
}
#[derive(Getters, Clone, Eq, PartialEq, Debug)]
#[getter(as_copy)]
pub struct ControlBlockFactory {
    internal_pk: InternalPk,
    output_pk: OutputPk,
    parity: Parity,
    merkle_root: TapNodeHash,
    #[getter(skip)]
    merkle_path: TapMerklePath,
    #[getter(skip)]
    remaining_leaves: Vec<LeafInfo>,
}
impl ControlBlockFactory {
    #[inline]
    pub fn with(internal_pk: InternalPk, tap_tree: TapTree) -> Self {
        let merkle_root = tap_tree.merkle_root();
        let (output_pk, parity) = internal_pk.to_output_pk(Some(merkle_root));
        ControlBlockFactory {
            internal_pk,
            output_pk,
            parity,
            merkle_root,
            merkle_path: empty!(),
            remaining_leaves: tap_tree.into_vec(),
        }
    }
    #[inline]
    pub fn into_remaining_leaves(self) -> Vec<LeafInfo> { self.remaining_leaves }
}
impl Iterator for ControlBlockFactory {
    type Item = (ControlBlock, LeafScript);
    fn next(&mut self) -> Option<Self::Item> {
        let leaf = self.remaining_leaves.pop()?;
        let leaf_script = leaf.script;
        let control_block = ControlBlock::with(
            leaf_script.version,
            self.internal_pk,
            self.parity,
            self.merkle_path.clone(),
        );
        Some((control_block, leaf_script))
    }
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
#[cfg_attr(
    feature = "serde",
    derive(Serialize, Deserialize),
    serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct TapDerivation {
    pub leaf_hashes: Vec<TapLeafHash>,
    pub origin: KeyOrigin,
}
impl TapDerivation {
    pub fn with_internal_pk(xpub_origin: XpubOrigin, terminal: Terminal) -> Self {
        let origin = KeyOrigin::with(xpub_origin, terminal);
        TapDerivation {
            leaf_hashes: empty!(),
            origin,
        }
    }
}