use std::fmt;
use std::ops::Deref;

use sequoia_openpgp as openpgp;

use openpgp::Result;

use crate::Certification;
use crate::Depth;
use crate::CertSynopsis;

/// A path in a Network.
///
/// A path is a valid sequence of [`Certification`]s.
//
// This is technically a path prefix.
//
// While building a path, path constraints are enforced.  In
// particular, when appending a certification, we check that adding
// the new certification will not introduce a cycle, the issuer
// matches the previous certification's target, and the depth is
// sufficient.
//
// Note: we do not check the regular expressions!  Regular
// expressions only apply to the target.
#[derive(Clone)]
pub struct Path {
    // The root.
    root: CertSynopsis,

    // Then the transition from the previous node to the next, and the
    // next node.
    edges: Vec<Certification>,

    // Residual depth.  To append a certification, this must be >0.
    // After adding a new certification, the new residual depth is:
    // min(residual_depth - 1, certification.depth).
    residual_depth: Depth,
}

impl fmt::Debug for Path {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let indent = f.precision().unwrap_or(0);
        let indent: String = vec![ ' '; indent ].into_iter().collect();

        f.write_fmt(format_args!(
            "Path [\n"))?;

        f.write_fmt(format_args!(
            "{}  {} ({})\n",
            indent,
            self.root.fingerprint(),
            self.root.primary_userid().map(|userid| {
                String::from_utf8_lossy(userid.value()).into_owned()
            }).unwrap_or_else(|| "[no User ID]".into())))?;

        for certification in self.edges.iter() {
            f.write_fmt(format_args!(
                "{}           |\n", indent))?;
            f.write_fmt(format_args!(
                "{}           | depth: {}\n", indent, certification.depth()))?;
            f.write_fmt(format_args!(
                "{}           | amount: {}\n", indent, certification.amount()))?;
            f.write_fmt(format_args!(
                "{}           | regexes: {}\n",
                indent,
                if certification.regular_expressions().matches_everything() {
                    String::from("*")
                } else {
                    format!("{:?}", &certification.regular_expressions())
                }))?;
            f.write_fmt(format_args!(
                "{}           v\n", indent))?;
            f.write_fmt(format_args!(
                "{}  {} ({})\n",
                indent, certification.target().fingerprint(),
                certification.userid().map(|userid| {
                    String::from_utf8_lossy(userid.value()).into_owned()
                }).unwrap_or_else(|| "[no User ID]".into())))?;
        }
        f.write_fmt(format_args!("{}]", indent))?;

        Ok(())
    }
}

impl Path {
    /// Instantiates a path starting at the specified root.
    ///
    /// We assume that the root is ultimately trusted (its trust depth
    /// is unlimited and its trust amount is maximal).
    pub(crate) fn new(root: CertSynopsis) -> Self
    {
        Self {
            root: root,

            // Most paths will be direct (one edge) or via one trusted
            // introducer (two edges); meta-introducers are rarely
            // used.
            edges: Vec::with_capacity(2),

            // Unconstrained.
            residual_depth: Depth::new(None),
        }
    }

    /// Returns the path's root.
    pub fn root(&self) -> &CertSynopsis {
        &self.root
    }

    /// Returns the last node in the path.
    pub fn target(&self) -> &CertSynopsis {
        if self.edges.len() == 0 {
            &self.root
        } else {
            &self.edges[self.edges.len() - 1].target()
        }
    }

    /// Returns an iterator over the path's certificates (the nodes).
    ///
    /// The certificates are returned from the root towards the target.
    pub fn certificates<'a>(&'a self) -> impl Iterator<Item=&CertSynopsis> + 'a {
        std::iter::once(&self.root)
            .chain(self.edges.iter().map(|certification| {
                certification.target()
            }))
    }

    /// Returns the number of nodes in the path.
    pub fn len(&self) -> usize {
        1 + self.edges.len()
    }

    /// Returns the certifications.
    ///
    /// The certifications are returned from the root towards the target.
    pub fn certifications<'a>(&'a self) -> impl Iterator<Item=&Certification> {
        self.edges.iter()
    }

    /// Returns the residual trust depth.
    #[cfg(test)]
    pub(crate) fn residual_depth(&self) -> Depth {
        self.residual_depth
    }

    /// Returns the amount that the target is trusted.
    ///
    /// 120 usually means fully trusted.
    pub fn amount(&self) -> usize {
        self.edges.iter().map(|e| e.amount()).min().unwrap_or(120) as usize
    }

    /// Appends certification to the path if the path allows it.
    ///
    /// A path may not allow it if the trust depth is insufficient.
    /// Paths are also not allowed to contain cycles.
    pub(crate) fn try_append(&mut self, certification: Certification)
        -> Result<()>
    {
        tracer!(false, "Path::try_append");
        t!("  path: {:?}", self);
        t!("  certification: {:?}", certification);

        if self.target().fingerprint() != certification.issuer().fingerprint() {
            return Err(anyhow::format_err!(
                "Can't add certification to path: \
                 the path's tail ({}) is not the certification's issuer ({})",
                self.target().fingerprint(), certification.issuer()));
        }

        if self.residual_depth == 0.into() {
            return Err(anyhow::format_err!("Not enough depth"));
        }

        // Check for cycles.  The last two nodes can target the same
        // certificate, but then target User IDs must be different.
        if self.root.fingerprint() == certification.target().fingerprint()
            || self.edges.iter()
                .enumerate()
                .any(|(i, c)| {
                    if c.target().fingerprint()
                        == certification.target().fingerprint()
                    {
                        if i == self.edges.len() - 1 {
                            c.userid() == certification.userid()
                        } else {
                            true
                        }
                    } else {
                        false
                    }
                })
        {
            return Err(anyhow::format_err!(
                "Adding {} to the path would create a cycle",
                certification.target()));
        }

        self.residual_depth = std::cmp::min(
            self.residual_depth.decrease(1), certification.depth());
        self.edges.push(certification);

        Ok(())
    }
}

/// A collection of paths.
///
/// The amount is the amount while respecting the total capacity of
/// the edges.
///
/// # Examples
///
/// Consider the following network (a number next to an edge is that
/// edge's trust amount):
///
/// ```text
///        root
///    60 /    \ 60
///      v      v
///    alice   bob
///    60 \    / 60
///        v  v
///       carol
///         | 90
///         v
///       target
/// ```
///
/// If we consider the following two paths: `root -> alice -> carol ->
/// target` and `root -> bob -> carol -> target`, then they each have
/// a trust amount of 60.  But taken together they only have a trust
/// amount of 90, because the edge `carol -> target` is shared, and
/// its capacity is 90.
#[derive(Clone)]
pub struct Paths {
    paths: Vec<(Path, usize)>,
}

impl Paths {
    pub(crate) fn new() -> Self {
        Self {
            paths: Vec::new(),
        }
    }

    /// Returns an iterator over the paths.
    ///
    /// Returns an iterator over each path and its trust amount.
    pub fn iter(&self) -> impl Iterator<Item=&(Path, usize)> {
        self.paths.iter()
    }

    /// The aggregate trust amount.
    ///
    /// This respects the network's capacity.  Thus, if multiple paths
    /// use the same edge, the total trust amount may be less than
    /// simple the trust amount of each individual path.
    pub fn amount(&self) -> usize {
        self.paths.iter().map(|(_, a)| a).sum()
    }

    pub(crate) fn push(&mut self, path: Path, amount: usize) {
        assert!(amount <= path.amount());

        self.paths.push((path, amount));
    }
}

impl Deref for Paths {
    type Target=[(Path, usize)];

    fn deref(&self) -> &Self::Target {
        &self.paths[..]
    }
}

impl fmt::Debug for Paths {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let indent = f.precision().unwrap_or(0);
        let indent: String = vec![ ' '; indent ].into_iter().collect();

        f.write_fmt(format_args!("Paths [\n"))?;
        for (i, (p, a)) in self.iter().enumerate() {
            f.write_fmt(format_args!(
                "{}  PATH #{}, trust amount: {}: {:.*?}\n",
                indent, i, a, indent.len() + 2, p))?;
        }
        f.write_fmt(format_args!("{}]", indent))?;
        Ok(())
    }
}
