use super::{GoodreadsResponse, Work};
use std::convert::From;

#[derive(Debug)]
pub struct GoodreadsApi {
    token: String,
}

impl GoodreadsApi {
    pub fn with_token(token: &str) -> GoodreadsApi {
        GoodreadsApi {
            token: String::from(token),
        }
    }

    fn get(&self, url: &str, params: &[(&str, &str)]) -> Result<GoodreadsResponse, GoodreadsError> {
        let url = reqwest::Url::parse_with_params(url, params)?;

        let client = reqwest::blocking::Client::builder()
            .user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
            .build()?;

        let resp = retry::retry(retry::delay::Fixed::from_millis(100).take(3), || {
            client.get(url.to_owned()).send()
        })?;

        let text: String = resp.text()?;
        let goodreads_response: GoodreadsResponse = serde_xml_rs::from_str(&text)?;

        Ok(goodreads_response)
    }

    pub fn get_books(&self, query: &str) -> Result<Vec<Work>, GoodreadsError> {
        if query.is_empty() {
            return Ok(Vec::new());
        }

        let goodreads_response = self.get(
            "https://www.goodreads.com/search/index.xml",
            &[("key", &self.token), ("q", query)],
        )?;

        Ok(goodreads_response.search.results_container.results)
    }
}

#[derive(Debug)]
#[allow(dead_code)]
#[allow(clippy::enum_variant_names)]
pub enum GoodreadsError {
    ReqwestError(reqwest::Error),
    ParseError(ammonia::url::ParseError),
    SerdeXmlError(serde_xml_rs::Error),
    RetryError(retry::Error<reqwest::Error>),
}

impl From<reqwest::Error> for GoodreadsError {
    fn from(e: reqwest::Error) -> Self {
        GoodreadsError::ReqwestError(e)
    }
}

impl From<ammonia::url::ParseError> for GoodreadsError {
    fn from(e: ammonia::url::ParseError) -> Self {
        GoodreadsError::ParseError(e)
    }
}

impl From<serde_xml_rs::Error> for GoodreadsError {
    fn from(e: serde_xml_rs::Error) -> Self {
        GoodreadsError::SerdeXmlError(e)
    }
}

impl From<retry::Error<reqwest::Error>> for GoodreadsError {
    fn from(e: retry::Error<reqwest::Error>) -> Self {
        GoodreadsError::RetryError(e)
    }
}
