use reqwest::Method;

use super::{urlencode, Client};
use crate::error::Error;
use crate::types::{
    Customer, OauthExchangePayload, OauthExchangeResponse, OauthProvidersResponse,
    OtpRequestPayload, OtpVerifyPayload, OtpVerifyResponse,
};

impl Client {
    pub async fn request_otp(&self, payload: OtpRequestPayload<'_>) -> Result<(), Error> {
        self.do_request::<_, serde_json::Value>(
            Method::POST,
            "/api/auth/customer/otp/request",
            Some(&payload),
        )
        .await
        .map(|_| ())
    }

    pub async fn verify_otp(
        &mut self,
        payload: OtpVerifyPayload<'_>,
    ) -> Result<OtpVerifyResponse, Error> {
        let resp = self
            .do_request::<_, OtpVerifyResponse>(
                Method::POST,
                "/api/auth/customer/otp/verify",
                Some(&payload),
            )
            .await?;
        self.set_customer_token(resp.access_token.clone());

        Ok(resp)
    }

    /// GET /api/me - current authenticated customer.
    pub async fn customer_me(&self) -> Result<Customer, Error> {
        self.do_request::<_, Customer>(Method::GET, "/api/me", None::<&()>)
            .await
    }

    pub async fn list_oauth_providers(
        &self,
        product: &str,
    ) -> Result<OauthProvidersResponse, Error> {
        let path = format!("/api/v1/products/{}/auth/providers", urlencode(product));
        self.do_request::<_, OauthProvidersResponse>(Method::GET, &path, None::<&()>)
            .await
    }

    pub async fn exchange_oauth_code(
        &mut self,
        payload: OauthExchangePayload<'_>,
    ) -> Result<OauthExchangeResponse, Error> {
        let resp = self
            .do_request::<_, OauthExchangeResponse>(
                Method::POST,
                "/api/auth/oauth/exchange",
                Some(&payload),
            )
            .await?;
        self.set_customer_token(resp.access_token.clone());

        Ok(resp)
    }
}
