use crate::valueset::ScimResolveStatus;
use std::iter::{self};

use super::ValueSet;
use crate::be::dbvalue::DbValueSetV2;
use crate::prelude::*;
use crate::value::{PartialValue, SyntaxType, Value};

use openssl::ec::EcKey;
use openssl::pkey::{Private, Public};

#[derive(Debug, Clone)]
struct EcKeyPrivate {
    priv_key: EcKey<Private>,
    pub_key: EcKey<Public>,
}

#[derive(Debug, Clone)]
pub struct ValueSetEcKeyPrivate {
    set: Option<EcKeyPrivate>,
}

impl ValueSetEcKeyPrivate {
    pub fn new(key: &EcKey<Private>) -> Box<Self> {
        #[allow(clippy::expect_used)]
        let pub_key = Self::private_key_to_public_key(key).expect(
            "Unable to retrieve public key from private key, likely corrupted. You must restore from backup.",
        );

        Box::new(ValueSetEcKeyPrivate {
            set: Some(EcKeyPrivate {
                priv_key: key.clone(),
                pub_key,
            }),
        })
    }

    fn push(&mut self, key: &EcKey<Private>) -> bool {
        #[allow(clippy::expect_used)]
        let pub_key = Self::private_key_to_public_key(key).expect(
            "Unable to retrieve public key from private key, likely corrupted. You must restore from backup.",
        );
        self.set = Some(EcKeyPrivate {
            priv_key: key.clone(),
            pub_key,
        });
        true
    }

    fn valueset_from_key_der(key_der: &[u8]) -> Result<ValueSet, OperationError> {
        let option_key = EcKey::private_key_from_der(key_der);
        if let Ok(key) = option_key {
            Ok(Self::new(&key))
        } else {
            Err(OperationError::InvalidDbState)
        }
    }

    fn private_key_to_public_key(private_key: &EcKey<Private>) -> Option<EcKey<Public>> {
        let public_key = private_key.public_key();
        let group = private_key.group();
        EcKey::from_public_key(group, public_key).ok()
    }

    pub fn from_dbvs2(key_der: &[u8]) -> Result<ValueSet, OperationError> {
        Self::valueset_from_key_der(key_der)
    }
}

impl ValueSetT for ValueSetEcKeyPrivate {
    fn insert_checked(&mut self, value: crate::value::Value) -> Result<bool, OperationError> {
        match value {
            Value::EcKeyPrivate(k) => Ok(self.push(&k)),
            _ => {
                debug_assert!(false);
                Err(OperationError::InvalidValueState)
            }
        }
    }

    fn clear(&mut self) {
        self.set = None;
    }

    fn remove(&mut self, _pv: &crate::value::PartialValue, _cid: &Cid) -> bool {
        false
    }

    fn contains(&self, _pv: &crate::value::PartialValue) -> bool {
        false
    }

    fn substring(&self, _pv: &crate::value::PartialValue) -> bool {
        false
    }

    fn startswith(&self, _pv: &PartialValue) -> bool {
        false
    }

    fn endswith(&self, _pv: &PartialValue) -> bool {
        false
    }

    fn lessthan(&self, _pv: &crate::value::PartialValue) -> bool {
        false
    }

    fn len(&self) -> usize {
        1
    }

    fn generate_idx_eq_keys(&self) -> Vec<String> {
        Vec::with_capacity(0)
    }

    fn syntax(&self) -> SyntaxType {
        SyntaxType::EcKeyPrivate
    }

    fn validate(&self, _schema_attr: &crate::schema::SchemaAttribute) -> bool {
        match self.set.as_ref() {
            Some(key) => key.priv_key.check_key().is_ok() && key.pub_key.check_key().is_ok(),
            None => false,
        }
    }

    fn to_proto_string_clone_iter(&self) -> Box<dyn Iterator<Item = String> + '_> {
        Box::new(iter::once(String::from("hidden")))
    }

    fn to_scim_value(&self) -> Option<ScimResolveStatus> {
        None
    }

    fn to_db_valueset_v2(&self) -> DbValueSetV2 {
        #[allow(clippy::expect_used)]
        let key_der = self
            .set
            .as_ref()
            .map(|key| {
                key.priv_key.private_key_to_der().expect(
        "Unable to process eckey to der, likely corrupted. You must restore from backup.",
    )
            })
            .unwrap_or_default();
        DbValueSetV2::EcKeyPrivate(key_der)
    }

    fn to_partialvalue_iter(&self) -> Box<dyn Iterator<Item = crate::value::PartialValue> + '_> {
        Box::new(iter::once(PartialValue::SecretValue))
    }

    fn to_value_iter(&self) -> Box<dyn Iterator<Item = crate::value::Value> + '_> {
        match &self.set {
            Some(key) => Box::new(iter::once(Value::EcKeyPrivate(key.priv_key.clone()))),
            None => Box::new(iter::empty::<Value>()),
        }
    }

    fn equal(&self, other: &super::ValueSet) -> bool {
        #[allow(clippy::expect_used)]
        other.as_ec_key_private().map_or(false, |other_key| {
            self.set.as_ref().map_or(false, |key| {
                key.priv_key
                    .private_key_to_der()
                    .expect("Failed to retrieve key der")
                    == other_key
                        .private_key_to_der()
                        .expect("Failed to retrieve key der")
            })
        })
    }

    fn merge(&mut self, other: &super::ValueSet) -> Result<(), OperationError> {
        if let Some(other_key) = other.as_ec_key_private() {
            let priv_key = other_key.clone();
            let pub_key = Self::private_key_to_public_key(&priv_key)
                .ok_or(OperationError::CryptographyError)?;
            self.set = Some(EcKeyPrivate { pub_key, priv_key });
            Ok(())
        } else {
            debug_assert!(false);
            Err(OperationError::InvalidValueState)
        }
    }

    fn as_ec_key_private(&self) -> Option<&EcKey<Private>> {
        match self.set.as_ref() {
            Some(key) => Some(&key.priv_key),
            None => None,
        }
    }

    fn to_eckey_private_single(&self) -> Option<&EcKey<Private>> {
        match self.set.as_ref() {
            Some(key) => Some(&key.priv_key),
            None => None,
        }
    }

    fn to_eckey_public_single(&self) -> Option<&EcKey<Public>> {
        match self.set.as_ref() {
            Some(key) => Some(&key.pub_key),
            None => None,
        }
    }
}
