use serde::{
  Deserialize, Deserializer,
  de::{Visitor, value::SeqAccessDeserializer},
};

use crate::entities::EnvironmentVar;

pub fn labels_deserializer<'de, D>(
  deserializer: D,
) -> Result<String, D::Error>
where
  D: Deserializer<'de>,
{
  deserializer.deserialize_any(LabelVisitor)
}

pub fn option_labels_deserializer<'de, D>(
  deserializer: D,
) -> Result<Option<String>, D::Error>
where
  D: Deserializer<'de>,
{
  deserializer.deserialize_any(OptionLabelVisitor)
}

struct LabelVisitor;

impl<'de> Visitor<'de> for LabelVisitor {
  type Value = String;

  fn expecting(
    &self,
    formatter: &mut std::fmt::Formatter,
  ) -> std::fmt::Result {
    write!(formatter, "string or Vec<EnvironmentVar>")
  }

  fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
  where
    E: serde::de::Error,
  {
    let out = v.to_string();
    if out.is_empty() || out.ends_with('\n') {
      Ok(out)
    } else {
      Ok(out + "\n")
    }
  }

  fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
  where
    A: serde::de::SeqAccess<'de>,
  {
    let vars = Vec::<EnvironmentVar>::deserialize(
      SeqAccessDeserializer::new(seq),
    )?;
    let vars = vars
      .iter()
      .map(|EnvironmentVar { variable, value }| {
        format!("  {variable}: {value}")
      })
      .collect::<Vec<_>>()
      .join("\n");
    let extra = if vars.is_empty() { "" } else { "\n" };
    Ok(vars + extra)
  }
}

struct OptionLabelVisitor;

impl<'de> Visitor<'de> for OptionLabelVisitor {
  type Value = Option<String>;

  fn expecting(
    &self,
    formatter: &mut std::fmt::Formatter,
  ) -> std::fmt::Result {
    write!(formatter, "null or string or Vec<EnvironmentVar>")
  }

  fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
  where
    E: serde::de::Error,
  {
    LabelVisitor.visit_str(v).map(Some)
  }

  fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
  where
    A: serde::de::SeqAccess<'de>,
  {
    LabelVisitor.visit_seq(seq).map(Some)
  }

  fn visit_none<E>(self) -> Result<Self::Value, E>
  where
    E: serde::de::Error,
  {
    Ok(None)
  }

  fn visit_unit<E>(self) -> Result<Self::Value, E>
  where
    E: serde::de::Error,
  {
    Ok(None)
  }
}
