use crate::RatError;
use std::str::FromStr;

pub struct Endpoint {
    url: reqwest::Url,
    method: reqwest::Method,
    verbose: Option<bool>,
}

impl Endpoint {
    fn parse_method(method: &str) -> Result<reqwest::Method, RatError> {
        use reqwest::Method;
        let method = method.to_lowercase();
        let method = match method.as_ref() {
            "get" => Method::GET,
            "post" => Method::POST,
            "put" => Method::PUT,
            "patch" => Method::PATCH,
            "delete" => Method::DELETE,
            "head" => Method::HEAD,
            "options" => Method::OPTIONS,
            _ => return Err(RatError::InvalidRestMethod(method)),
        };

        Ok(method)
    }

    fn parse_url(url: &str) -> Result<reqwest::Url, RatError> {
        match reqwest::Url::from_str(url) {
            Ok(u) => Ok(u),
            Err(e) => Err(RatError::InvalidUrl(e.to_string())),
        }
    }

    pub fn new(url: &str, method: &str, verbose: Option<bool>) -> Result<Self, RatError> {
        let method = Self::parse_method(method)?;
        let url = Self::parse_url(url)?;
        Ok(Self {
            url,
            method,
            verbose,
        })
    }

    pub fn build(self) -> reqwest::blocking::Request {
        let req = reqwest::blocking::Request::new(self.method, self.url);
        if self.verbose == Some(true) {
            log::debug!("{:#?}", req);
        }
        req
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn endpoint_parsing_works() -> Result<(), RatError> {
        let url = "https://google.com";
        for m in vec!["get", "post", "put", "delete", "patch", "head", "options"] {
            assert!(Endpoint::new(url, m, None).is_ok());
            assert!(Endpoint::new(url, m.to_uppercase().as_ref(), None).is_ok());
        }

        assert_eq!(Endpoint::parse_method("get")?, reqwest::Method::GET);

        assert!(matches!(
            Endpoint::new(url, "invalid", None),
            Err(RatError::InvalidRestMethod(_))
        ));

        assert!(matches!(
            Endpoint::new("not-a-valid-url", "get", None),
            Err(RatError::InvalidUrl(_))
        ));

        Ok(())
    }
}