use crate::{Error, secret::Password};
use aws_config::Region;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
pub struct S3Secret {
  pub hostname: Option<String>,
  pub access_key_id: Password,
  pub secret_access_key: Password,
  pub role_name: Option<Password>,
  pub session_name: Option<String>,
  pub region: Option<String>,
  pub bucket: String,
}

impl S3Secret {
  /// NB: hostname as endpoint url is not working well with AWS S3, it is anyway deduced from region
  pub fn hostname(&self) -> Option<String> {
    self
      .hostname
      .as_ref()
      .map(|hostname| !hostname.contains("amazonaws.com"))
      .unwrap_or_default()
      .then_some(self.hostname.clone()?)
  }

  pub fn access_key(&self) -> &str {
    &self.access_key_id.0
  }

  pub fn secret_key(&self) -> &str {
    &self.secret_access_key.0
  }

  pub fn role_name(&self) -> Option<String> {
    self
      .role_name
      .as_ref()
      .map(|role_name| role_name.0.to_string())
  }

  pub fn session_name(&self) -> &Option<String> {
    &self.session_name
  }

  pub fn region_as_string(&self) -> &Option<String> {
    &self.region
  }

  pub fn bucket(&self) -> &str {
    &self.bucket
  }

  pub fn region(&self) -> Result<Option<Region>, Error> {
    match self.region_as_string() {
      Some(region) => Ok(Some(Region::new(region.to_string()))),
      None => Ok(None),
    }
  }
}

#[test]
pub fn test_s3_secret_getters() {
  let hostname = Some("s3.server.name".to_string());
  let access_key_id = "S3_ACCESS_KEY".to_string();
  let secret_access_key = "S3_SECRET_KEY".to_string();
  let region = Some("s3-region".to_string());
  let role_name = "s3-role".to_string();
  let session_name = Some("s3-session".to_string());
  let bucket = "s3-bucket".to_string();

  let secret = S3Secret {
    hostname: hostname.clone(),
    access_key_id: Password(access_key_id.clone()),
    secret_access_key: Password(secret_access_key.clone()),
    region: region.clone(),
    role_name: Some(Password(role_name.clone())),
    session_name: session_name.clone(),
    bucket: bucket.clone(),
  };

  println!("secret: {secret:?}");

  assert_eq!(secret.hostname(), hostname);
  assert_eq!(secret.access_key(), &access_key_id);
  assert_eq!(secret.secret_key(), &secret_access_key);
  assert_eq!(secret.region_as_string(), &region);
  assert_eq!(secret.role_name(), Some(role_name));
  assert_eq!(secret.session_name(), &session_name);
  assert_eq!(secret.bucket(), &bucket);

  let secret = S3Secret {
    hostname: hostname.clone(),
    access_key_id: Password(access_key_id.clone()),
    secret_access_key: Password(secret_access_key.clone()),
    region: region.clone(),
    role_name: None,
    session_name: None,
    bucket: bucket.clone(),
  };

  assert_eq!(secret.role_name(), None);
}

#[test]
pub fn test_s3_secret_get_region() {
  let secret = S3Secret {
    hostname: Some("s3.server.name".to_string()),
    access_key_id: Password::from("s3_access_key"),
    secret_access_key: Password::from("s3_secret_key"),
    region: Some("s3-region".to_string()),
    role_name: None,
    session_name: None,
    bucket: "".to_string(),
  };

  let expected = Some(Region::new("s3-region"));
  let region = secret.region().unwrap();
  assert_eq!(region, expected);
}

#[test]
pub fn test_s3_secret_get_region_no_hostname() {
  let secret = S3Secret {
    hostname: None,
    access_key_id: Password::from("s3_access_key"),
    secret_access_key: Password::from("s3_secret_key"),
    region: Some("eu-west-3".to_string()),
    role_name: None,
    session_name: None,
    bucket: "".to_string(),
  };

  let expected = Some(Region::new("eu-west-3"));
  let region = secret.region().unwrap();
  assert_eq!(region, expected);
}

#[test]
pub fn test_s3_secret_get_region_no_region() {
  let secret = S3Secret {
    hostname: Some("s3.server.name".to_string()),
    access_key_id: Password::from("s3_access_key"),
    secret_access_key: Password::from("s3_secret_key"),
    region: None,
    role_name: None,
    session_name: None,
    bucket: "".to_string(),
  };

  let expected = None;
  let region = secret.region().unwrap();
  assert_eq!(region, expected);
}

#[test]
pub fn test_s3_secret_get_region_no_hostname_nor_region() {
  let secret = S3Secret {
    hostname: None,
    access_key_id: Password::from("s3_access_key"),
    secret_access_key: Password::from("s3_secret_key"),
    region: None,
    role_name: None,
    session_name: None,
    bucket: "".to_string(),
  };

  let region = secret.region().unwrap();
  assert_eq!(region, None);
}
