use std::borrow::Borrow;

use sequoia_openpgp as openpgp;
use openpgp::Fingerprint;

use crate::Root;

/// A list of trust roots.
///
/// This describes a list of trust roots.
///
/// The roots are sorted and deduped by fingerprint.  If there are
/// multiple entries for the same root, then the entry with the
/// largest trust amount is used.
#[derive(Debug, Clone)]
pub struct Roots {
    roots: Vec<Root>,
}

impl Roots {
    /// Returns a new `Root`.
    ///
    /// The roots are sorted and deduped.  If there are multiple
    /// entries for the same root, then the entry with the largest
    /// trust amount is used.
    pub fn new<I>(roots: I) -> Self
    where I: IntoIterator,
          I::Item: Into<Root>,
    {
        let mut roots: Vec<Root>
            = roots.into_iter().map(Into::into).collect();
        if roots.len() > 1 {
            roots.sort_by_key(|r| r.fingerprint().clone());

            roots.dedup_by(|other, keep| {
                if other.fingerprint() == keep.fingerprint() {
                    // Prefer the higher trust.
                    keep.set_amount(std::cmp::max(other.amount(), keep.amount()));
                    true
                } else {
                    false
                }
            });
        }

        Self {
            roots,
        }
    }

    /// Returns an empty set of roots.
    pub fn empty() -> Self {
        Roots {
            roots: Vec::new(),
        }
    }

    /// Returns whether or not there are any roots.
    pub fn is_empty(&self) -> bool {
        self.roots.is_empty()
    }

    /// Returns an iterator over the roots.
    ///
    /// The roots are returned in sorted order.
    pub fn iter(&self) -> impl Iterator<Item=&Root> {
        self.roots.iter()
    }

    /// Returns whether the specified certificate is a root.
    pub fn is_root<F>(&self, fpr: F) -> bool
        where F: Borrow<Fingerprint>
    {
        let fpr = fpr.borrow();
        self.roots.binary_search_by_key(&fpr, |r| &r.fingerprint()).is_ok()
    }

    /// Returns the specified root.
    pub fn get<F>(&self, fpr: F) -> Option<&Root>
        where F: Borrow<Fingerprint>
    {
        let fpr = fpr.borrow();
        self.roots.binary_search_by_key(&fpr, |r| r.fingerprint())
            .ok()
            .map(|i| &self.roots[i])
    }

    /// Retains only the roots specified by the predicate.
    ///
    /// Identical to [`Vec::retain`].
    pub fn retain<F>(&mut self, f: F)
    where
        F: FnMut(&Root) -> bool
    {
        self.roots.retain(f);
    }
}

macro_rules! gen_from {
    ($t:ty) => {
        impl From<$t> for Roots {
            fn from(roots: $t) -> Self {
                Roots::new(roots.iter().map(Root::from))
            }
        }

    };
}

gen_from!(&[Fingerprint]);
gen_from!(&[Fingerprint; 0]);
gen_from!(&[Fingerprint; 1]);
gen_from!(&[Fingerprint; 2]);
gen_from!(&[Fingerprint; 3]);
gen_from!(&[Fingerprint; 4]);
gen_from!(&[Fingerprint; 5]);
gen_from!(&[Fingerprint; 6]);
gen_from!(&[Fingerprint; 7]);
gen_from!(&[Fingerprint; 8]);
gen_from!(&[Fingerprint; 9]);
gen_from!(&[Fingerprint; 10]);
gen_from!(&[Fingerprint; 11]);
gen_from!(&[Fingerprint; 12]);
gen_from!(&[Fingerprint; 13]);
gen_from!(&[Fingerprint; 14]);
gen_from!(&[Fingerprint; 15]);
gen_from!(&[Fingerprint; 16]);

gen_from!(&[(Fingerprint, usize)]);
gen_from!(&[(Fingerprint, usize); 0]);
gen_from!(&[(Fingerprint, usize); 1]);
gen_from!(&[(Fingerprint, usize); 2]);
gen_from!(&[(Fingerprint, usize); 3]);
gen_from!(&[(Fingerprint, usize); 4]);
gen_from!(&[(Fingerprint, usize); 5]);
gen_from!(&[(Fingerprint, usize); 6]);
gen_from!(&[(Fingerprint, usize); 7]);
gen_from!(&[(Fingerprint, usize); 8]);
gen_from!(&[(Fingerprint, usize); 9]);
gen_from!(&[(Fingerprint, usize); 10]);
gen_from!(&[(Fingerprint, usize); 11]);
gen_from!(&[(Fingerprint, usize); 12]);
gen_from!(&[(Fingerprint, usize); 13]);
gen_from!(&[(Fingerprint, usize); 14]);
gen_from!(&[(Fingerprint, usize); 15]);
gen_from!(&[(Fingerprint, usize); 16]);

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

    use crate::FULLY_TRUSTED;
    use crate::PARTIALLY_TRUSTED;

    #[test]
    fn roots() {
        let fpr_a: Fingerprint =
            "A7319A9B166AB530A5FBAC8AB43CA77F7C176AF4"
           .parse().expect("valid fingerprint");
        let fpr_b: Fingerprint =
            "BFC5CA10FB55A4B790E2A1DBA5CFAB9A9E34E183"
           .parse().expect("valid fingerprint");

        let input = &[
            (fpr_a.clone(), PARTIALLY_TRUSTED),
            (fpr_a.clone(), FULLY_TRUSTED),
            (fpr_b.clone(), FULLY_TRUSTED),
        ];

        // Test all orderings of input.
        for i in 0..3 {
            for j in 0..3 {
                for k in 0..3 {
                    if i == j || i == k || j == k {
                        continue;
                    }

                    let r = Roots::new(&[
                        input[i].clone(),
                        input[j].clone(),
                        input[k].clone(),
                    ]);
                    assert_eq!(
                        &[
                            (fpr_a.clone(), FULLY_TRUSTED),
                            (fpr_b.clone(), FULLY_TRUSTED),
                        ][..],
                        &r.iter()
                            .map(|r| (r.fingerprint().clone(), r.amount()))
                            .collect::<Vec<_>>());
                }
            }
        }
    }
}
