use serde::Deserializer;
use serde::Serializer;
use std::borrow::Borrow;
use std::fmt::Debug;
use std::hash::Hash;
use std::sync::Arc;
use url::Url;
use v8::NewStringType;
pub enum FastString {
    Static(&'static str),
    StaticAscii(&'static str),
      Owned(Box<str>),
    Arc(Arc<str>),
}
impl FastString {
      const fn is_ascii(s: &'static [u8]) -> bool {
    let mut i = 0;
    while i < s.len() {
      if !s[i].is_ascii() {
        return false;
      }
      i += 1;
    }
    true
  }
      pub const fn from_static(s: &'static str) -> Self {
    if Self::is_ascii(s.as_bytes()) {
      Self::StaticAscii(s)
    } else {
      Self::Static(s)
    }
  }
      pub const fn ensure_static_ascii(s: &'static str) -> Self {
    if Self::is_ascii(s.as_bytes()) {
      Self::StaticAscii(s)
    } else {
      panic!("This string contained non-ASCII characters and cannot be created with ensure_static_ascii")
    }
  }
      pub fn into_cheap_copy(self) -> (Self, Self) {
    match self {
      Self::Static(s) => (Self::Static(s), Self::Static(s)),
      Self::StaticAscii(s) => (Self::StaticAscii(s), Self::StaticAscii(s)),
      Self::Arc(s) => (Self::Arc(s.clone()), Self::Arc(s)),
      Self::Owned(s) => {
        let s: Arc<str> = s.into();
        (Self::Arc(s.clone()), Self::Arc(s))
      }
    }
  }
    pub fn try_clone(&self) -> Option<Self> {
    match self {
      Self::Static(s) => Some(Self::Static(s)),
      Self::StaticAscii(s) => Some(Self::StaticAscii(s)),
      Self::Arc(s) => Some(Self::Arc(s.clone())),
      Self::Owned(_s) => None,
    }
  }
  pub const fn try_static_ascii(&self) -> Option<&'static [u8]> {
    match self {
      Self::StaticAscii(s) => Some(s.as_bytes()),
      _ => None,
    }
  }
  pub fn as_bytes(&self) -> &[u8] {
        match self {
      Self::Arc(s) => s.as_bytes(),
      Self::Owned(s) => s.as_bytes(),
      Self::Static(s) => s.as_bytes(),
      Self::StaticAscii(s) => s.as_bytes(),
    }
  }
  pub fn as_str(&self) -> &str {
        match self {
      Self::Arc(s) => s,
      Self::Owned(s) => s,
      Self::Static(s) => s,
      Self::StaticAscii(s) => s,
    }
  }
      pub fn v8<'a>(
    &self,
    scope: &mut v8::HandleScope<'a>,
  ) -> v8::Local<'a, v8::String> {
    match self.try_static_ascii() {
      Some(s) => v8::String::new_external_onebyte_static(scope, s).unwrap(),
      None => {
        v8::String::new_from_utf8(scope, self.as_bytes(), NewStringType::Normal)
          .unwrap()
      }
    }
  }
    pub fn truncate(&mut self, index: usize) {
    match self {
      Self::Static(b) => *self = Self::Static(&b[..index]),
      Self::StaticAscii(b) => *self = Self::StaticAscii(&b[..index]),
      Self::Owned(b) => *self = Self::Owned(b[..index].to_owned().into()),
            Self::Arc(s) => *self = s[..index].to_owned().into(),
    }
  }
  pub(crate) fn v8_string<'a>(
    &self,
    scope: &mut v8::HandleScope<'a>,
  ) -> Option<v8::Local<'a, v8::String>> {
    if let Some(code) = self.try_static_ascii() {
      v8::String::new_external_onebyte_static(scope, code)
    } else {
      v8::String::new_from_utf8(
        scope,
        self.as_bytes(),
        v8::NewStringType::Normal,
      )
    }
  }
}
impl Hash for FastString {
  fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
    self.as_str().hash(state)
  }
}
impl AsRef<str> for FastString {
  fn as_ref(&self) -> &str {
    self.as_str()
  }
}
impl Borrow<str> for FastString {
  fn borrow(&self) -> &str {
    self.as_str()
  }
}
impl Debug for FastString {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    Debug::fmt(self.as_str(), f)
  }
}
impl Default for FastString {
  fn default() -> Self {
    Self::StaticAscii("")
  }
}
impl PartialEq for FastString {
  fn eq(&self, other: &Self) -> bool {
    self.as_bytes() == other.as_bytes()
  }
}
impl Eq for FastString {}
impl From<Url> for FastString {
  fn from(value: Url) -> Self {
    let s: String = value.into();
    s.into()
  }
}
impl From<String> for FastString {
  fn from(value: String) -> Self {
    FastString::Owned(value.into_boxed_str())
  }
}
impl From<Arc<str>> for FastString {
  fn from(value: Arc<str>) -> Self {
    FastString::Arc(value)
  }
}
impl serde::Serialize for FastString {
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  where
    S: Serializer,
  {
    serializer.serialize_str(self.as_str())
  }
}
type DeserializeProxy<'de> = &'de str;
impl<'de> serde::Deserialize<'de> for FastString {
  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  where
    D: Deserializer<'de>,
  {
    DeserializeProxy::<'de>::deserialize(deserializer)
      .map(|v| v.to_owned().into())
  }
}
#[macro_export]
macro_rules! include_ascii_string {
  ($file:literal) => {
    $crate::FastString::ensure_static_ascii(::std::include_str!($file))
  };
}
#[macro_export]
macro_rules! ascii_str {
  ($str:literal) => {
    $crate::FastString::ensure_static_ascii($str)
  };
}
#[cfg(test)]
mod tests {
  use super::*;
  #[test]
  fn truncate() {
    let mut s = "123456".to_owned();
    s.truncate(3);
    let mut code: FastString = FastString::from_static("123456");
    code.truncate(3);
    assert_eq!(s, code.as_ref());
    let mut code: FastString = "123456".to_owned().into();
    code.truncate(3);
    assert_eq!(s, code.as_ref());
    let arc_str: Arc<str> = "123456".into();
    let mut code: FastString = arc_str.into();
    code.truncate(3);
    assert_eq!(s, code.as_ref());
  }
}