use core::fmt::Debug;
use core::fmt::Formatter;
use identity_core::common::KeyComparable;
use crate::verification_method::VerificationMethod;
use identity_did::CoreDID;
use identity_did::DIDUrl;
#[derive(Clone, PartialEq, Eq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum MethodRef {
  Embed(VerificationMethod),
  Refer(DIDUrl),
}
impl MethodRef {
  pub fn id(&self) -> &DIDUrl {
    match self {
      Self::Embed(inner) => inner.id(),
      Self::Refer(inner) => inner,
    }
  }
  pub fn controller(&self) -> Option<&CoreDID> {
    match self {
      Self::Embed(inner) => Some(inner.controller()),
      Self::Refer(_) => None,
    }
  }
  #[inline]
  pub fn is_embedded(&self) -> bool {
    matches!(self, Self::Embed(_))
  }
  #[inline]
  pub fn is_referred(&self) -> bool {
    matches!(self, Self::Refer(_))
  }
  pub fn map<F>(self, f: F) -> MethodRef
  where
    F: FnMut(CoreDID) -> CoreDID,
  {
    match self {
      MethodRef::Embed(method) => MethodRef::Embed(method.map(f)),
      MethodRef::Refer(id) => MethodRef::Refer(id.map(f)),
    }
  }
  pub fn try_map<F, E>(self, f: F) -> Result<MethodRef, E>
  where
    F: FnMut(CoreDID) -> Result<CoreDID, E>,
  {
    Ok(match self {
      MethodRef::Embed(method) => MethodRef::Embed(method.try_map(f)?),
      MethodRef::Refer(id) => MethodRef::Refer(id.try_map(f)?),
    })
  }
  pub fn try_into_embedded(self) -> Result<VerificationMethod, Box<Self>> {
    match self {
      Self::Embed(inner) => Ok(inner),
      Self::Refer(_) => Err(self.into()),
    }
  }
  pub fn try_into_referenced(self) -> Result<DIDUrl, Box<Self>> {
    match self {
      Self::Embed(_) => Err(self.into()),
      Self::Refer(inner) => Ok(inner),
    }
  }
}
impl Debug for MethodRef {
  fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
    match self {
      Self::Embed(inner) => Debug::fmt(inner, f),
      Self::Refer(inner) => Debug::fmt(inner, f),
    }
  }
}
impl From<VerificationMethod> for MethodRef {
  #[inline]
  fn from(other: VerificationMethod) -> Self {
    Self::Embed(other)
  }
}
impl From<DIDUrl> for MethodRef {
  #[inline]
  fn from(other: DIDUrl) -> Self {
    Self::Refer(other)
  }
}
impl AsRef<DIDUrl> for MethodRef {
  #[inline]
  fn as_ref(&self) -> &DIDUrl {
    self.id()
  }
}
impl KeyComparable for MethodRef {
  type Key = DIDUrl;
  #[inline]
  fn key(&self) -> &Self::Key {
    self.id()
  }
}