mod cursor;
mod ftp;
mod gcs;
mod http;
mod one_drive;
mod s3;
mod sftp;

pub use self::cursor::CursorSecret;
pub use self::ftp::FtpSecret;
pub use self::gcs::{GcsCredential, GcsSecret};
pub use self::http::HttpSecret;
pub use self::one_drive::OneDriveSecret;
pub use self::s3::S3Secret;
pub use self::sftp::SftpSecret;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize, Serializer};
use std::fmt::{Debug, Display, Formatter};

#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[derive(Default)]
pub enum Secret {
  Ftp(FtpSecret),
  Gcs(GcsSecret),
  Http(HttpSecret),
  #[default]
  Local,
  S3(S3Secret),
  Sftp(SftpSecret),
  Cursor(CursorSecret),
  OneDrive(OneDriveSecret),
}

#[derive(Clone, Deserialize, Eq, JsonSchema, PartialEq)]
pub struct Password(String);

impl From<String> for Password {
  fn from(value: String) -> Self {
    Self(value)
  }
}

impl From<&str> for Password {
  fn from(value: &str) -> Self {
    Self(value.to_string())
  }
}

impl Display for Password {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    write!(f, "{}", self.0.clone())
  }
}

impl Debug for Password {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    write!(f, "*****")
  }
}

impl Serialize for Password {
  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  where
    S: Serializer,
  {
    serializer.serialize_str("*****")
  }
}

#[test]
pub fn test_secret_default() {
  assert_eq!(Secret::default(), Secret::Local);
}

#[test]
pub fn test_secret_ftp() {
  let json_str = r#"{
    "type": "ftp",
    "hostname": "ftp://ftp_server_name",
    "username": "Johnny",
    "password": "B_g00d"
  }"#;
  let expected = Secret::Ftp(FtpSecret {
    hostname: "ftp://ftp_server_name".to_string(),
    port: None,
    secure: None,
    username: Some("Johnny".to_string()),
    password: Some(Password::from("B_g00d")),
    root_directory: None,
  });
  let secret: Secret = serde_json::from_str(json_str).unwrap();
  assert_eq!(secret, expected);

  let serialized = serde_json::to_string(&secret).unwrap();
  assert_eq!(
    serialized,
    r#"{"type":"ftp","hostname":"ftp://ftp_server_name","port":null,"secure":null,"username":"Johnny","password":"*****","root_directory":null}"#
      .to_string()
  )
}

#[test]
pub fn test_secret_http() {
  let json_str = r#"{
    "type": "http",
    "endpoint": "http://www.hostname.com",
    "method": "POST",
    "headers": "{\"content-type\": \"application/json\"}",
    "body": "{\"Johnny\": \"Ca$h\"}"
  }"#;
  let expected = Secret::Http(HttpSecret {
    endpoint: Some("http://www.hostname.com".to_string()),
    method: Some("POST".to_string()),
    headers: Some("{\"content-type\": \"application/json\"}".to_string()),
    body: Some("{\"Johnny\": \"Ca$h\"}".to_string()),
  });
  let secret: Secret = serde_json::from_str(json_str).unwrap();
  assert_eq!(secret, expected);
}

#[test]
pub fn test_secret_local() {
  let json_str = r#"{
    "type": "local"
  }"#;
  let expected = Secret::Local;
  let secret: Secret = serde_json::from_str(json_str).unwrap();
  assert_eq!(secret, expected);
}

#[test]
pub fn test_secret_cursor() {
  let json_str = r#"{
    "type": "cursor"
  }"#;
  let expected = Secret::Cursor(CursorSecret { content: None });
  let secret: Secret = serde_json::from_str(json_str).unwrap();
  assert_eq!(secret, expected);
}

#[test]
pub fn test_secret_s3() {
  let json_str = r#"{
    "type": "s3",
    "hostname": "s3.server.name",
    "access_key_id": "123_ACCESS_KEY",
    "secret_access_key": "456_SECRET_KEY",
    "bucket": "johnny"
  }"#;
  let expected = Secret::S3(S3Secret {
    hostname: Some("s3.server.name".to_string()),
    access_key_id: Password::from("123_ACCESS_KEY"),
    secret_access_key: Password::from("456_SECRET_KEY"),
    region: None,
    role_name: None,
    session_name: None,
    bucket: "johnny".to_string(),
  });
  let secret: Secret = serde_json::from_str(json_str).unwrap();
  assert_eq!(secret, expected);
}

#[test]
pub fn test_secret_sftp() {
  let json_str = r#"{
    "type": "sftp",
    "hostname": "127.0.0.1",
    "username": "Johnny",
    "password": "B_g00d"
  }"#;
  let expected = Secret::Sftp(SftpSecret {
    hostname: "127.0.0.1".to_string(),
    port: None,
    username: "Johnny".to_string(),
    password: Some(Password::from("B_g00d")),
    private_key: None,
    private_key_file_path: None,
    known_host: None,
    root_directory: None,
  });
  let secret: Secret = serde_json::from_str(json_str).unwrap();
  assert_eq!(secret, expected);
}

#[test]
pub fn test_secret_gcs() {
  let json_str = r#"{
    "type": "gcs",
    "bucket": "test_bucket",
    "credential": {
      "type": "service_account",
      "project_id": "rs_transfer",
      "private_key_id": "0123456789abcdefghijklmnopqrstuvwxyz",
      "private_key": "-----BEGIN PRIVATE KEY-----\n0123456789abcdefghijklmnopqrstuvwxyz\n-----END PRIVATE KEY-----\n",
      "client_email": "johnny@B_g00d.iam.gserviceaccount.com",
      "client_id": "0123456789abcdefghijklmnopqrstuvwxyz",
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token",
      "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
      "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/johnny%B_g00d.iam.gserviceaccount.com"
    }
  }"#;
  let expected = Secret::Gcs(GcsSecret {
    bucket: "test_bucket".to_string(),
    credential: GcsCredential {
      gcs_type: "service_account".to_string(),
      project_id: "rs_transfer".to_string(),
      private_key_id: Password::from("0123456789abcdefghijklmnopqrstuvwxyz"),
      private_key: Password::from(
        "-----BEGIN PRIVATE KEY-----\n0123456789abcdefghijklmnopqrstuvwxyz\n-----END PRIVATE KEY-----\n",
      ),
      client_email: "johnny@B_g00d.iam.gserviceaccount.com".to_string(),
      client_id: "0123456789abcdefghijklmnopqrstuvwxyz".to_string(),
      auth_uri: "https://accounts.google.com/o/oauth2/auth".to_string(),
      token_uri: "https://oauth2.googleapis.com/token".to_string(),
      auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs".to_string(),
      client_x509_cert_url:
        "https://www.googleapis.com/robot/v1/metadata/x509/johnny%B_g00d.iam.gserviceaccount.com"
          .to_string(),
    },
  });
  let secret: Secret = serde_json::from_str(json_str).unwrap();
  assert_eq!(secret, expected);
}

#[test]
pub fn test_secret_one_drive() {
  let json_str = r#"{
    "type": "one_drive",
    "client_id": "526de9b4-71ed-4011-926c-5fce0860ac1f",
    "token": "EwBgA8l6BAAUkj1NuJYtTVha+Mogk+HEiPbQo04AAUqdUq2zM+VzFWKHSKRyKpc6kt8edQXu2Y"
  }"#;

  let expected = Secret::OneDrive(OneDriveSecret {
    client_id: "526de9b4-71ed-4011-926c-5fce0860ac1f".to_string(),
    token: Password::from(
      "EwBgA8l6BAAUkj1NuJYtTVha+Mogk+HEiPbQo04AAUqdUq2zM+VzFWKHSKRyKpc6kt8edQXu2Y",
    ),
  });

  let secret: Secret = serde_json::from_str(json_str).unwrap();
  assert_eq!(secret, expected);
}
