use std::borrow::Borrow;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
use std::time::SystemTime;

use crossbeam::thread;
use crossbeam::channel::unbounded as channel;

use sequoia_openpgp as openpgp;

use openpgp::Result;
use openpgp::cert::prelude::*;
use openpgp::Fingerprint;
use openpgp::KeyHandle;
use openpgp::KeyID;
use openpgp::packet::UserID;
use openpgp::policy::Policy;
use openpgp::regex::RegexSet;

use crate::Certification;
use crate::CertificationSet;
use crate::CertSynopsis;
use crate::RevocationStatus;

pub(crate) mod filter;
mod root;
pub use root::Root;
mod roots;
pub use roots::Roots;
mod rooted;
pub use rooted::RootedNetwork;

use super::TRACE;

/// A certification network.
pub struct Network {
    // The list of Certificates.
    pub(crate) nodes: HashMap<Fingerprint, CertSynopsis>,

    // Certifications that a certificate has made.
    //
    // Example:
    //
    // If certificate 0xA signed two User IDs, B and B', on 0xB, and
    // it signed one User ID, C, on 0xC, 0xA would map to a vector
    // containing two (not three!) CertificationSets: one for 0xA's
    // certifications of User IDs on 0xB and another for 0xA's
    // certifications on User IDs of 0xC.
    //
    // Note: if a certificate has not certified any other key, it will
    // NOT appear here.  But, it will appear in `nodes`.
    pub(crate) edges: HashMap<Fingerprint, Vec<CertificationSet>>,

    // Certifications on a certificate.
    //
    // Example:
    //
    //   C = 0xA certifies <Bob, 0xB>.
    //
    // Whereas `edges` contains the entry 0xA with a CertificationSet
    // containing the certificate C, redges contains an entry for 0xB
    // with a CertificateSet containing C.
    pub(crate) redges: HashMap<Fingerprint, Vec<CertificationSet>>,

    // The reference time.
    reference_time: SystemTime,
}

impl fmt::Debug for Network {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Network {{\n")?;
        write!(f, "  Reference time: {:?}\n", self.reference_time)?;
        write!(f, "  Nodes:\n")?;

        let mut certs: Vec<_> = self.nodes.iter().map(|(fpr, cert)| {
            (
                cert.userids()
                    .map(|userid| {
                        String::from_utf8_lossy(userid.value())
                            .into_owned()
                    })
                    .collect::<Vec<String>>()
                    .join(", "),
                fpr
            )
        }).collect();
        certs.sort();

        for (userid, fpr) in certs {
            write!(f, "    {}: {}\n", fpr, userid)?;
        }

        write!(f, "  Edges:\n")?;

        let mut edges: Vec<_> = self.edges.values().collect();
        edges.sort_by_key(|cs| {
            self.nodes.get(&cs[0].issuer().fingerprint())
                .unwrap()
                .primary_userid()
                .map(|userid| {
                    String::from_utf8_lossy(userid.value()).into_owned()
                })
                .unwrap_or("[no User ID]".into())
        });

        for cs in edges {
            let issuer = &cs[0].issuer();

            write!(f, "    {} certifies:\n", issuer)?;
            for (userid, cs) in cs.iter().flat_map(|cs| cs.certifications()) {
                for c in cs {
                    write!(f, "      {}, {}: {}, {}, {}\n",
                           c.target().fingerprint(),
                           userid.as_ref().map(|userid| {
                               String::from_utf8_lossy(userid.value()).into_owned()
                           }).unwrap_or_else(|| "<No User ID>".into()),
                           c.depth(), c.amount(),
                           if c.regular_expressions().matches_everything() { "*".into() }
                           else { format!("{:?}", c.regular_expressions()) })?;
                }
            }
        }

        write!(f, "}}\n")?;

        Ok(())
    }
}

impl Network {
    /// Returns an empty network.
    fn empty(reference_time: SystemTime) -> Self {
        Network {
            nodes: HashMap::new(),
            edges: HashMap::new(),
            redges: HashMap::new(),
            reference_time,
        }
    }

    /// Builds a web of trust network from a set of certificates.
    ///
    /// If a certificate is invalid according to the policy, the
    /// certificate is silently ignored.
    pub fn from_certs<I, C, T>(certs: I, policy: &dyn Policy, t: T)
        -> Result<Self>
        where I: Iterator<Item=C>,
              C: Into<Cert>,
              T: Into<Option<SystemTime>>,
    {
        tracer!(TRACE, "Network::from_certs");

        let t = t.into().unwrap_or_else(|| SystemTime::now());
        let certs: Vec<Cert> = certs.map(Into::into).collect();
        let mut vcs: Vec<ValidCert> = Vec::new();

        for cert in certs.iter() {
            match cert.with_policy(policy, Some(t)) {
                Ok(vc) => vcs.push(vc),
                Err(err) => {
                    t!("Ignoring cert {}, which is not valid \
                        according to the policy: {}",
                       cert.fingerprint(), err);
                }
            }
        }

        Self::from_valid_certs(vcs, t)
    }

    /// Builds a web of trust network from a set of valid
    /// certificates.
    ///
    /// This assumes that all certificates use the same policy and the
    /// same reference time, t.
    fn from_valid_certs(mut vcs: Vec<ValidCert>, t: SystemTime)
        -> Result<Self>
    {
        tracer!(TRACE, "Network::from_valid_certs");

        // Avoid an extra level of indentation.
        thread::scope(|thread_scope| {

        // The threads.  We start them on demand.
        let threads = if vcs.len() < 16 {
            // The keyring is small, limit the number of threads.
            2
        } else {
            num_cpus::get()
        };

        // Use at least one and not more than we have cores.
        let threads = std::cmp::min(
            std::cmp::max(1, threads),
            std::cmp::max(1, num_cpus::get()));

        // Sort the certificates so those with the most third-party
        // certificates come first.  By doing the units of work that
        // are likely to take longest first, we are more likely to
        // keep all threads occupied at the very end.
        if threads > 1 {
            vcs.sort_by_cached_key(|vc| {
                vc.userids()
                    .map(|ua| -(ua.certifications().count() as isize))
                    .sum::<isize>()
            });
        }


        // Maps from `Fingerprint`s to `ValidCert`s and from `KeyID`s
        // to `ValidCert`s.
        //
        // We need the Key ID variant for signatures that only include
        // an Issuer subpacket.
        let mut by_fpr: HashMap<Fingerprint, &ValidCert>
            = HashMap::new();
        let mut by_keyid: HashMap<KeyID, Vec<&ValidCert>>
            = HashMap::new();

        for vc in vcs.iter() {
            // Build the maps.
            by_fpr.entry(vc.fingerprint())
                .or_insert(&vc);
            by_keyid.entry(vc.keyid())
                .and_modify(|e| e.push(&vc))
                .or_insert(vec![ &vc ]);
        }

        let by_fpr = Arc::new(by_fpr);
        let by_keyid = Arc::new(by_keyid);

        let doit = |ua: &ValidUserIDAmalgamation,
                    by_fpr: &HashMap<Fingerprint, &ValidCert>,
                    by_keyid: &HashMap<KeyID, Vec<&ValidCert>>|
            -> Vec<Certification>
        {
            let mut valid_certifications: Vec<Certification> = Vec::new();

            let target = ua.cert();

            // Skip invalid User IDs.
            if let Err(_) = std::str::from_utf8(ua.userid().value()) {
                t!("{}: Non-UTF-8 User ID ({:?}) skipped.",
                   target.keyid(),
                   String::from_utf8_lossy(ua.userid().value()));
                return valid_certifications;
            }

            // We iterate over all of the certifications.  We need to
            // be careful: we only want the newest certification for a
            // given <Issuer, <Cert, UserID>> tuple.

            let mut certifications: Vec<_> = ua.certifications().collect();
            t!("<{}, {}>: {} third-party certifications",
               target.fingerprint(), String::from_utf8_lossy(ua.userid().value()),
               certifications.len());

            // Sort the certifications so that the newest comes first.
            certifications.sort_by(|a, b| {
                a.signature_creation_time().cmp(&b.signature_creation_time())
                    .reverse()
            });

            // If we've already seen a valid certification from the
            // Issuer on the current <Cert, UserID> binding.
            let mut seen: HashMap<Fingerprint, std::time::SystemTime>
                = HashMap::new();

            'cert: for certification in certifications {
                // Check that the certification is valid:
                //
                //   - Find the issuer.
                //   - Verify the signature.
                //
                // If we don't have a certificate for the alleged issuer,
                // then we ignore the certification.

                let certification_time =
                    if let Some(t) = certification.signature_creation_time() {
                        t
                    } else {
                        continue;
                    };

                let verify = |possible_issuer: &ValidCert|
                                 -> Result<Certification>
                {
                    certification
                        .clone()
                        .verify_userid_binding(
                            possible_issuer.primary_key().key(),
                            target.primary_key().key(),
                            ua.userid())?;

                    // Ignore if the issuer is not alive at the
                    // certification time (not the reference time!).
                    let possible_issuer_then
                        = possible_issuer.clone().with_policy(
                            possible_issuer.policy(), certification_time)?;

                    if let Err(err) = possible_issuer_then.alive() {
                        t!("Skipping certification {:02X}{:02X}: issuer \
                            was not alive at certification time.",
                           certification.digest_prefix()[0],
                           certification.digest_prefix()[1]);

                        return Err(err.context(
                            "issuer not alive at certification time"));
                    }

                    // Ignore if the issuer was revoked at the
                    // certification time (not the reference time!).
                    let rs: RevocationStatus
                        = possible_issuer_then
                        .revocation_status().into();
                    match rs {
                        RevocationStatus::Hard => {
                            t!("Skipping certification {:02X}{:02X}: issuer \
                                was hard revoked.",
                               certification.digest_prefix()[0],
                               certification.digest_prefix()[1]);
                            return Err(
                                anyhow::anyhow!("issuer is hard revoked"));
                        }
                        RevocationStatus::Soft(rev_time) => {
                            if rev_time <= certification_time {
                                t!("Skipping certification {:02X}{:02X}: issuer \
                                    was soft revoked at certification time.",
                                   certification.digest_prefix()[0],
                                   certification.digest_prefix()[1]);
                                return Err(
                                    anyhow::anyhow!("issuer was soft revoked"));
                            }
                        }
                        RevocationStatus::NotAsFarAsWeKnow => (),
                    }

                    let issuer: KeyHandle
                        = possible_issuer.fingerprint().into();

                    // Ignore if the issuer later revoked the
                    // certification.
                    for rev in ua.other_revocations() {
                        // All User ID revocations are soft
                        // revocations.  So if the User ID was later
                        // recertified, that's okay.
                        if let Some(rev_time) = rev.signature_creation_time() {
                            if rev_time > t {
                                // Revocation is not yet live.  Ignore it.
                                continue;
                            }
                            if rev_time <= certification_time {
                                // Certification is newer than the
                                // revocation.  Ignore the revocation.
                                continue;
                            }
                        } else {
                            // Invalid signature.
                            continue;
                        };

                        // We explicitly ignore any expiration on
                        // revocations.

                        // Check that the issuer actually issued this
                        // revocation.
                        if rev.get_issuers().iter().any(|kh| {
                            kh.aliases(&issuer)
                        }) {
                            if let Ok(()) = rev
                                .clone()
                                .verify_userid_revocation(
                                    possible_issuer.primary_key().key(),
                                    target.primary_key().key(),
                                    ua.userid())
                            {
                                t!("issuer revoked certification, ignoring");
                                return Err(anyhow::anyhow!(
                                    "issuer revoked certification"));
                            }
                        }
                    }


                    let (depth, amount, re_set) = if let Some((d, a))
                        = certification.trust_signature()
                    {
                        (d, a, RegexSet::from_signature(certification)
                             .expect("internal error"))
                    } else {
                        (0, 120, RegexSet::everything()
                             .expect("internal error"))
                    };

                    t!("<{}, {}> {} <{}, {}> \
                        (depth: {}, amount: {}, scope: {:?})",
                       possible_issuer.keyid(),
                       possible_issuer
                           .primary_userid()
                           .map(|ua| {
                               String::from_utf8_lossy(ua.value()).into_owned()
                           })
                           .unwrap_or("[no User ID]".into()),
                           if depth > 0 {
                               "tsigned"
                           } else {
                               "certified"
                           },
                       target.keyid(),
                       String::from_utf8_lossy(ua.userid().value()),
                       depth,
                       amount,
                       if re_set.matches_everything() { "*".into() }
                       else { format!("{:?}", re_set) });

                    Certification::from_signature(
                        possible_issuer, Some(ua.userid().clone()), target,
                        certification)
                };

                // Ignore if the certification is not alive at the
                // reference time.
                if t < certification_time {
                    t!("Skipping certification {:02X}{:02X}: \
                        created ({:?}) after reference time ({:?}).",
                       certification.digest_prefix()[0],
                       certification.digest_prefix()[1],
                       certification_time, t);
                    continue;
                }
                if let Some(e) = certification.signature_expiration_time() {
                    if e <= t {
                        t!("Skipping certification {:02X}{:02X}: \
                            expired ({:?}) as of reference time ({:?}).",
                           certification.digest_prefix()[0],
                           certification.digest_prefix()[1],
                           e, t);
                        continue;
                    }
                }

                let target_then =
                    match target.clone()
                        .with_policy(target.policy(), certification_time)
                    {
                        Ok(vc) => vc,
                        Err(err) => {
                            t!("Skipping certification {:02X}{:02X}: target \
                                was not valid at certification time: {}.",
                               certification.digest_prefix()[0],
                               certification.digest_prefix()[1],
                               err);
                            continue;
                        }
                    };

                // Ignore if the target is not alive at the
                // certification time (not the reference time!).
                if let Err(err) = target_then.alive() {
                    t!("Skipping certification {:02X}{:02X}: target \
                        not alive at certification time: {}.",
                       certification.digest_prefix()[0],
                       certification.digest_prefix()[1],
                       err);
                    continue;
                }

                // Ignore if the target was revoked at the
                // certification time (not the reference time!).
                let rs: RevocationStatus
                    = target_then.revocation_status().into();
                match rs {
                    RevocationStatus::Hard => {
                        t!("Skipping certification {:02X}{:02X}: target \
                            was hard revoked at certification time.",
                           certification.digest_prefix()[0],
                           certification.digest_prefix()[1]);
                        continue;
                    }
                    RevocationStatus::Soft(rev_time) => {
                        if rev_time <= certification_time {
                            t!("Skipping certification {:02X}{:02X}: target \
                                was soft revoked at certification time.",
                               certification.digest_prefix()[0],
                               certification.digest_prefix()[1]);
                            continue;
                        }
                    }
                    RevocationStatus::NotAsFarAsWeKnow => (),
                }

                // Improve tracing: distinguish between we don't have
                // the issuer's certificate and we have it, but the
                // signature is invalid.
                let mut invalid_sig: Option<Fingerprint> = None;
                for alleged_issuer in certification.get_issuers() {
                    match alleged_issuer {
                        KeyHandle::Fingerprint(issuer_fpr) => {
                            if let Some(alleged_issuer) = by_fpr.get(&issuer_fpr) {
                                if let Ok(certification) = verify(alleged_issuer) {
                                    if let Some(saw) = seen.get(&issuer_fpr) {
                                        if saw > &certification_time
                                        {
                                            // We already have a newer
                                            // certification from this
                                            // issuer.
                                            t!("Skipping certification \
                                                by {} for <{:?}, {}> at {:?}: \
                                                saw a newer one.",
                                               issuer_fpr,
                                               ua.userid(), target.keyid(),
                                               certification.creation_time());
                                            continue 'cert;
                                        }
                                    }

                                    t!("Using certification \
                                        by {} for <{:?}, {}> at {:?}: \
                                        {}/{}.",
                                       issuer_fpr,
                                       ua.userid(), target.keyid(),
                                       certification.creation_time(),
                                       certification.depth(),
                                       certification.amount());

                                    valid_certifications.push(certification);
                                    seen.insert(issuer_fpr,
                                                certification_time);

                                    continue 'cert;
                                } else {
                                    invalid_sig = Some(issuer_fpr);
                                }
                            }
                        }
                        KeyHandle::KeyID(keyid) => {
                            if let Some(alleged_issuers) = by_keyid.get(&keyid) {
                                for alleged_issuer in alleged_issuers {
                                    let issuer_fpr = alleged_issuer.fingerprint();
                                    if let Ok(certification) = verify(alleged_issuer) {
                                        if let Some(saw) = seen.get(&issuer_fpr) {
                                            if saw > &certification_time
                                            {
                                                // We already have a newer
                                                // certification from this
                                                // issuer.
                                                t!("Skipping certification \
                                                    by {} for <{:?}, {}> at {:?}: \
                                                    saw a newer one.",
                                                   issuer_fpr,
                                                   ua.userid(), target.keyid(),
                                                   certification.creation_time());
                                                continue 'cert;
                                            }
                                        }

                                        t!("Using certification \
                                            by {} for <{:?}, {}> at {:?}: \
                                            {}/{}.",
                                           issuer_fpr,
                                           ua.userid(), target.keyid(),
                                           certification.creation_time(),
                                           certification.depth(),
                                           certification.amount());

                                        valid_certifications.push(certification);
                                        seen.insert(issuer_fpr.clone(),
                                                    certification_time);

                                        continue 'cert;
                                    } else {
                                        invalid_sig = Some(issuer_fpr);
                                    }
                                }
                            }
                        }
                    };
                }

                if let Some(keyid) = invalid_sig {
                    t!("Invalid certification {:02X}{:02X} by {} for <{:?}, {}>.",
                       certification.digest_prefix()[0],
                       certification.digest_prefix()[1],
                       keyid,
                       ua.userid(), target.keyid());
                } else {
                    t!("Certification {:02X}{:02X} for <{:?}, {}>: \
                        missing issuer's certificate ({}).",
                       certification.digest_prefix()[0],
                       certification.digest_prefix()[1],
                       ua.userid(), target.keyid(),
                       certification.get_issuers()
                           .first()
                           .map(|h| h.to_string())
                           .unwrap_or("(no issuer subkeys)".into())
                    );
                }
            }

            valid_certifications
        };


        // A communication channel for sending work to the workers.
        let (work_tx, work_rx) = channel();

        let mut threads_extant = Vec::new();

        for ua in vcs.iter().flat_map(|vc| vc.userids()) {
            if threads_extant.len() < threads {
                let tid = threads_extant.len();
                t!("Starting thread {} of {}",
                   tid, threads);

                // The thread's state.
                let work_rx = work_rx.clone();
                let by_fpr = Arc::clone(&by_fpr);
                let by_keyid = Arc::clone(&by_keyid);

                threads_extant.push(thread_scope.spawn(move |_| {
                    // We move the first work item to avoid taking the
                    // lock.
                    let mut work = Some(Ok(ua));
                    let mut results = Vec::new();

                    loop {
                        match work.take().unwrap_or_else(|| work_rx.recv()) {
                            Err(_) => break,
                            Ok(ua) => {
                                t!("Thread {} dequeuing {}!",
                                   tid, ua.cert().fingerprint());
                                let result = doit(&ua, &by_fpr, &by_keyid);
                                results.extend(result);
                            }
                        }
                    }

                    t!("Thread {} exiting", tid);

                    results
                }));
            } else {
                work_tx.send(ua).unwrap();
            }
        }

        // When the threads see this drop, they will exit.
        drop(work_tx);

        let mut results: Vec<Certification>
            = Vec::with_capacity(2 * vcs.len());
        for t in threads_extant.into_iter() {
            let result = t.join().unwrap();
            results.extend(result);
        }

        Self::new(vcs.iter(), results.into_iter(), t)

        }).unwrap()
    }

    /// Builds a web of trust network from a set of certifications.
    ///
    /// The set of certificates are inferred from the certifications.
    /// However, not all certificates certify or are certified.  Thus,
    /// it is possible to include the certificates.  This is primarily
    /// relevant when the certificate is question is a trust root, and
    /// we want to authenticate any self signatures.
    ///
    /// If you have OpenPGP data, you are better off using
    /// [`Network::from_certs`].
    pub fn new<I, C, J, D>(certs: I, certifications: J, t: SystemTime)
        -> Result<Self>
    where I: Iterator<Item=C>,
          C: Into<CertSynopsis>,
          J: Iterator<Item=D>,
          D: Into<Certification>,
    {
        tracer!(TRACE, "Network::new");

        let mut n = Network::empty(t);

        let mut certs_hash
            = HashMap::from_iter(certs.map(|c| {
                let c = c.into();
                (c.fingerprint(), c)
            }));
        for c in certifications {
            let c = c.into();

            let issuer = c.issuer();
            let target = c.target();

            certs_hash.insert(issuer.fingerprint(), issuer.clone());
            certs_hash.insert(target.fingerprint(), target.clone());

            match n.edges.entry(issuer.fingerprint()) {
                e @ Entry::Occupied(_) => {
                    // We merge below.
                    e.and_modify(|e| {
                        e.push(CertificationSet::from_certification(c.clone()))
                    });
                }
                e @ Entry::Vacant(_) => {
                    e.or_insert(
                        vec![
                            CertificationSet::from_certification(c.clone())
                        ]);
                }
            }

            match n.redges.entry(target.fingerprint()) {
                e @ Entry::Occupied(_) => {
                    // We merge below.
                    e.and_modify(|e| {
                        e.push(CertificationSet::from_certification(c.clone()))
                    });
                }
                e @ Entry::Vacant(_) => {
                    e.or_insert(
                        vec![
                            CertificationSet::from_certification(c.clone())
                        ]);
                }
            }
        }

        n.nodes = certs_hash;

        t!("Merging certifications.");

        // Merge the CertificationSets.  A certification is from a
        // certificate and over a certification and User ID pair.  We
        // want one CertificateSet for each pair of certificates.
        for (_, cs) in n.edges.iter_mut() {
            cs.sort_by(|a, b| {
                a.target().fingerprint().cmp(&b.target().fingerprint())
            });

            // Now merge certifications from the same certificate.
            *cs = cs.drain(..).fold(
                Vec::new(),
                |mut v: Vec<CertificationSet>, cs: CertificationSet|
                    -> Vec<CertificationSet>
                {
                    let len = v.len();
                    if len > 0 {
                        let l = &mut v[len-1];
                        if l.target().fingerprint() == cs.target().fingerprint() {
                            l.merge(cs);
                        } else {
                            v.push(cs);
                        }
                    } else {
                        v.push(cs);
                    }

                    v
                });
        }

        for (_, cs) in n.redges.iter_mut() {
            cs.sort_by(|a, b| {
                a.issuer().fingerprint().cmp(&b.issuer().fingerprint())
            });

            // Now merge certifications from the same certificate.
            *cs = cs.drain(..).fold(
                Vec::new(),
                |mut v: Vec<CertificationSet>, cs: CertificationSet| -> Vec<CertificationSet> {
                    let len = v.len();
                    if len > 0 {
                        let l = &mut v[len-1];
                        if l.issuer().fingerprint() == cs.issuer().fingerprint() {
                            l.merge(cs);
                        } else {
                            v.push(cs);
                        }
                    } else {
                        v.push(cs);
                    }

                    v
                });
        }

        t!("Done.");

        Ok(n)
    }

    /// Returns a reference to the certificate, if it exists in the network.
    pub fn certs(&self) -> impl Iterator<Item=&CertSynopsis>
    {
        self.nodes.values()
    }

    /// Returns a reference to the certificate, if it exists in the network.
    pub fn cert<F>(&self, fingerprint: F) -> Option<&CertSynopsis>
        where F: Borrow<Fingerprint>
    {
        self.nodes.get(fingerprint.borrow())
    }

    /// Returns all third-party certifications.
    ///
    /// This only returns the active certifications.  If a binding has
    /// been certified twice by the same key, then, as usual, the
    /// older binding is ignored.
    pub fn third_party_certifications(&self)
        -> impl Iterator<Item=&Certification>
    {
        self.edges.values().flat_map(|cs| {
            cs.iter().flat_map(|cs| {
                cs.certifications().flat_map(|(_userid, certifications)| {
                    certifications.iter()
                })
            })
        })
    }

    /// Returns all third-party certifications of the specified
    /// certificate.
    pub fn third_party_certifications_of(&self, cert: Fingerprint)
        -> impl Iterator<Item=&Certification>
    {
        const CS_EMPTY: Vec<CertificationSet> = Vec::new();
        const CS_EMPTY_REF: &Vec<CertificationSet> = &CS_EMPTY;

        let cs = if let Some(cs) = self.redges.get(&cert) {
            cs
        } else {
            CS_EMPTY_REF
        };

        cs.iter().flat_map(|cs| {
            cs.certifications().flat_map(|(_userid, certifications)| {
                certifications.iter()
            })
        })
    }

    /// Returns all User IDs that were certified for the specified
    /// certificate.
    ///
    /// This returns both self-signed User IDs, and User IDs certified
    /// by third-parties for the specified certificate.  The result is
    /// deduped.
    pub fn certified_userids_of(&self, cert: Fingerprint)
        -> Vec<UserID>
    {
        if let Some(cert) = self.nodes.get(&cert) {
            let mut userids: Vec<UserID> = self
                .third_party_certifications_of(cert.fingerprint())
                .filter_map(|c| c.userid())
                .chain(cert.userids().map(|u| u.userid()))
                .cloned()
                .collect();
            userids.sort_unstable();
            userids.dedup();
            userids
        } else {
            Vec::new()
        }
    }

    /// Returns all User IDs that were certified.
    ///
    /// This returns both self-signed User IDs, and User IDs certified
    /// by third-parties.  The result is deduped.
    pub fn certified_userids(&self)
        -> Vec<(Fingerprint, UserID)>
    {
        let mut userids = self.nodes.values().flat_map(|cert| {
            self
                .third_party_certifications_of(cert.fingerprint())
                .filter_map(|c| c.userid())
                .chain(cert.userids().map(|u| u.userid()))
                .cloned()
                .map(|userid| (cert.fingerprint(), userid))
        }).collect::<Vec<_>>();

        userids.sort_unstable();
        userids.dedup();
        userids
    }

    /// Returns the Network's reference time.
    pub fn reference_time(&self) -> SystemTime {
        self.reference_time
    }
}

#[cfg(test)]
mod test {
    use super::*;

    use openpgp::packet::UserID;
    use openpgp::parse::Parse;
    use openpgp::policy::StandardPolicy;

    #[allow(unused)]
    #[test]
    fn third_party_certifications() -> Result<()> {
        let p = &StandardPolicy::new();

        let alice_fpr: Fingerprint =
            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let bob_fpr: Fingerprint =
            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
           .parse().expect("valid fingerprint");
        let bob_uid
            = UserID::from("<bob@other.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
        let bob_some_org_uid
            = UserID::from("<bob@some.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA

        let carol_fpr: Fingerprint =
            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5

        let dave_fpr: Fingerprint =
            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@other.org>");
        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902


        let certs: Vec<Cert> = CertParser::from_bytes(
            &crate::testdata::data("multiple-userids-1.pgp"))?
            .map(|c| c.expect("Valid certificate"))
            .collect();
        let n = Network::from_certs(certs.into_iter(), p, None)?;

        eprintln!("{:?}", n);

        let mut c = n.third_party_certifications().collect::<Vec<_>>();
        assert_eq!(c.len(), 4);
        c.sort_by_key(|c| (c.userid(), c.issuer().fingerprint()));
        assert_eq!(&c[0].issuer().fingerprint(), &alice_fpr);
        assert_eq!(c[0].userid(), Some(&bob_uid));
        assert_eq!(&c[1].issuer().fingerprint(), &alice_fpr);
        assert_eq!(c[1].userid(), Some(&bob_some_org_uid));
        assert_eq!(&c[2].issuer().fingerprint(), &bob_fpr);
        assert_eq!(c[2].userid(), Some(&carol_uid));
        assert_eq!(&c[3].issuer().fingerprint(), &carol_fpr);
        assert_eq!(c[3].userid(), Some(&dave_uid));

        Ok(())
    }

    #[allow(unused)]
    #[test]
    fn third_party_certifications_of() -> Result<()> {
        let p = &StandardPolicy::new();

        let alice_fpr: Fingerprint =
            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let bob_fpr: Fingerprint =
            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
           .parse().expect("valid fingerprint");
        let bob_uid
            = UserID::from("<bob@other.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
        let bob_some_org_uid
            = UserID::from("<bob@some.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA

        let carol_fpr: Fingerprint =
            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5

        let dave_fpr: Fingerprint =
            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@other.org>");
        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902


        let certs: Vec<Cert> = CertParser::from_bytes(
            &crate::testdata::data("multiple-userids-1.pgp"))?
            .map(|c| c.expect("Valid certificate"))
            .collect();
        let n = Network::from_certs(certs.into_iter(), p, None)?;

        eprintln!("{:?}", n);

        // No one certified alice.
        assert!(
            n.third_party_certifications_of(alice_fpr.clone())
                .next()
                .is_none());

        // Alice (and no one else) certified each of Bob's User IDs.
        let mut c = n.third_party_certifications_of(bob_fpr)
            .collect::<Vec<_>>();
        assert_eq!(c.len(), 2);
        c.sort_by_key(|c| (c.issuer().fingerprint(),
                           c.userid()));
        assert_eq!(&c[0].issuer().fingerprint(), &alice_fpr);
        assert_eq!(c[0].userid(), Some(&bob_uid));
        assert_eq!(&c[1].issuer().fingerprint(), &alice_fpr);
        assert_eq!(c[1].userid(), Some(&bob_some_org_uid));

        Ok(())
    }

    #[allow(unused)]
    #[test]
    fn certified_userids_of() -> Result<()> {
        let p = &StandardPolicy::new();

        let alice_fpr: Fingerprint =
            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let bob_fpr: Fingerprint =
            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
           .parse().expect("valid fingerprint");
        let bob_uid
            = UserID::from("<bob@other.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
        let bob_some_org_uid
            = UserID::from("<bob@some.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA

        let carol_fpr: Fingerprint =
            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5

        let dave_fpr: Fingerprint =
            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@other.org>");
        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902


        let certs: Vec<Cert> = CertParser::from_bytes(
            &crate::testdata::data("multiple-userids-1.pgp"))?
            .map(|c| c.expect("Valid certificate"))
            .collect();
        let n = Network::from_certs(certs.into_iter(), p, None)?;

        eprintln!("{:?}", n);

        // There is the self signature.
        let mut c = n.certified_userids_of(alice_fpr);
        assert_eq!(c.len(), 1);

        // Alice (and no one else) certified each of Bob's User IDs
        // for the two self signed User ID.
        let mut c = n.certified_userids_of(bob_fpr);
        assert_eq!(c.len(), 2);
        c.sort_unstable();
        assert_eq!(&c[0], &bob_uid);
        assert_eq!(&c[1], &bob_some_org_uid);

        Ok(())
    }

    #[allow(unused)]
    #[test]
    fn certified_userids() -> Result<()> {
        let p = &StandardPolicy::new();

        let alice_fpr: Fingerprint =
            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
           .parse().expect("valid fingerprint");
        let alice_uid
            = UserID::from("<alice@example.org>");

        let bob_fpr: Fingerprint =
            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
           .parse().expect("valid fingerprint");
        let bob_uid
            = UserID::from("<bob@other.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
        let bob_some_org_uid
            = UserID::from("<bob@some.org>");
        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA

        let carol_fpr: Fingerprint =
            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
           .parse().expect("valid fingerprint");
        let carol_uid
            = UserID::from("<carol@example.org>");
        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5

        let dave_fpr: Fingerprint =
            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
           .parse().expect("valid fingerprint");
        let dave_uid
            = UserID::from("<dave@other.org>");
        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902


        let certs: Vec<Cert> = CertParser::from_bytes(
            &crate::testdata::data("multiple-userids-1.pgp"))?
            .map(|c| c.expect("Valid certificate"))
            .collect();
        let n = Network::from_certs(certs.into_iter(), p, None)?;

        eprintln!("{:?}", n);

        // Alice is the root, but self signatures count, so there are
        // five certified User IDs in this network.
        let mut got = n.certified_userids();
        assert_eq!(got.len(), 5);

        got.sort_unstable();

        let mut expected = [
            (alice_fpr.clone(), alice_uid.clone()),
            (bob_fpr.clone(), bob_uid.clone()),
            (bob_fpr.clone(), bob_some_org_uid.clone()),
            (carol_fpr.clone(), carol_uid.clone()),
            (dave_fpr.clone(), dave_uid.clone()),
        ];
        expected.sort_unstable();

        assert_eq!(got, expected);

        Ok(())
    }
}
