use fastly::http::{Method, StatusCode};
use fastly::{mime, Error, Request, Response};
use sha1::{Digest, Sha1};

// The name of the backend servers associated with this service.
// This must match the backend names you configured using `fastly backend create`.
static BACKEND_APP_SERVER: &str = "origin_0";
static BACKEND_SECURITY_CHECK: &str = "origin_1";

const PREFIX_LENGTH: usize = 5;

// Login form HTML
static LOGIN_HTML: &str = r#"<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Compromised password detection demo</title>
  </head>
  <body>
    <form action="/post" method="post">
      <div class="container">
        <label for="username"><b>Username</b></label>
        <input type="text" placeholder="Enter Username" name="username" required />

        <label for="password"><b>Password</b></label>
        <input type="password" placeholder="Enter Password" name="password" required />

        <button type="submit">Login</button>
      </div>
    </form>
  </body>
</html>"#;

#[derive(serde::Deserialize)]
struct BodyParams {
    password: Option<String>,
}

#[fastly::main]
fn main(mut req: Request) -> Result<Response, Error> {
    if let (&Method::GET, "/") = (req.get_method(), req.get_path()) {
        Ok(Response::from_status(StatusCode::OK)
            .with_content_type(mime::TEXT_HTML_UTF_8)
            .with_body(LOGIN_HTML))
    } else {
        let params = req.take_body_form::<BodyParams>().unwrap();
        if let Some(plain_cred) = params.password {
            // Generate sha1 hash of credential
            let mut hasher = Sha1::new();
            hasher.update(plain_cred.as_bytes());
            let hashed_cred = hex::encode_upper(hasher.finalize());

            // Split the hash of credential to left and right part at position PREFIX_LENGTH
            let hash_left = &hashed_cred[0..PREFIX_LENGTH];
            let hash_right = &hashed_cred[PREFIX_LENGTH..];

            // Prepare the request for threat check
            // (If you use HIBP in production please use an API key)
            let api_url = format!("https://api.pwnedpasswords.com/range/{hash_left}");
            let api_req = Request::get(api_url);

            // Send threat check request to API with the left-hand-side of the SHA1 hash
            let mut api_res = api_req.send(BACKEND_SECURITY_CHECK)?;
            let api_res_body = api_res.take_body_str();

            // Check if the response body contains the right-hand-side of the sha1 hash
            let result = if api_res_body.contains(hash_right) {
                "compromised-credential"
            } else {
                "safe-credential"
            };

            // Uncomment for debugging. For production use, avoid logging credentials
            // println!("Checked credential {plain_cred}, result is {result}");
            req.set_header("fastly-password-status", result);
        }

        Ok(req.send(BACKEND_APP_SERVER)?)
    }
}