use reqwest::header;

use crate::web_scrape::responses::LoginAjaxResponseType;
use crate::MangaDexClient;

/// Builder for the `/ajax/actions.ajax.php?function=login` endpoint.
pub struct LoginAjaxBuilder<'a> {
    md_client: &'a (dyn MangaDexClient + 'a),
    username: String,
    password: String,
    remember_me: bool,
    two_factor_code: Option<String>,
}

impl<'a> LoginAjaxBuilder<'a> {
    /// Create a new builder instance for logging in via the AJAX endpoint.
    ///
    /// # Parameters
    ///
    /// - `username`: MangaDex username.
    /// - `password`: Plain-text MangaDex password.
    pub fn new<T: Into<String>>(
        md_client: &'a dyn MangaDexClient,
        username: T,
        password: T,
    ) -> LoginAjaxBuilder<'a> {
        Self {
            md_client,
            username: username.into(),
            password: password.into(),
            remember_me: false,
            two_factor_code: None,
        }
    }

    /// Toggle the "Remember me" flag to increase the session length from 1 day to 1 year.
    pub fn remember_me(&mut self, remember_me: bool) -> &mut Self {
        self.remember_me = remember_me;
        self
    }

    /// Set the 2-factor 6-digit TOTP authentication code.
    ///
    /// This can be used in the same request as the login.
    pub fn two_factor<T: Into<String>>(&mut self, two_factor_code: T) -> &mut Self {
        self.two_factor_code = Some(two_factor_code.into());
        self
    }

    /// Send the request to MangaDex.
    ///
    /// If the response type is `LoginAjaxResponseType::TwoFactorAuthenticationRequired`,
    /// retry the request using the `two_factor()` method before sending to send the 2FA code.
    pub async fn send(&self) -> Result<LoginAjaxResponseType, reqwest::Error> {
        let mut headers = header::HeaderMap::new();
        headers.insert(
            "X-Requested-With",
            header::HeaderValue::from_static("XMLHttpRequest"),
        );
        headers.insert(header::ACCEPT, header::HeaderValue::from_static("*/*"));
        headers.insert(
            header::REFERER,
            header::HeaderValue::from_str(&format!(
                "{base_url}/login",
                base_url = self.md_client.base_url()
            ))
            .unwrap(),
        );
        headers.insert(
            header::ORIGIN,
            header::HeaderValue::from_str(&self.md_client.base_url()).unwrap(),
        );

        let mut body = Vec::new();
        body.push(("login_username", self.username.as_str()));
        body.push(("login_password", self.password.as_str()));
        body.push(("remember_me", if self.remember_me { "1" } else { "0" }));
        if let Some(two_factor_code) = &self.two_factor_code {
            body.push(("two_factor", two_factor_code.as_str()));
        }

        let resp = self
            .md_client
            .client()
            .post(&format!(
                "{base_url}/ajax/actions.ajax.php?function=login",
                base_url = self.md_client.base_url()
            ))
            .headers(headers)
            .form(&body)
            .send()
            .await?;

        Ok(LoginAjaxResponseType::from_response_body(
            resp.text().await?.as_str(),
        ))
    }
}
