use tracing::instrument;

use busbar_sf_client::security::url as url_security;

use crate::error::{Error, ErrorKind, Result};
use crate::user_password::{SetPasswordRequest, SetPasswordResponse, UserPasswordStatus};

impl super::SalesforceRestClient {
    /// Get the password expiration status for a user.
    #[instrument(skip(self))]
    pub async fn get_user_password_status(&self, user_id: &str) -> Result<UserPasswordStatus> {
        if !url_security::is_valid_salesforce_id(user_id) {
            return Err(Error::new(ErrorKind::Salesforce {
                error_code: "INVALID_ID".to_string(),
                message: "Invalid Salesforce ID format".to_string(),
            }));
        }
        let path = format!("sobjects/User/{}/password", user_id);
        self.client.rest_get(&path).await.map_err(Into::into)
    }

    /// Set a user's password.
    #[instrument(skip(self, request))]
    pub async fn set_user_password(
        &self,
        user_id: &str,
        request: &SetPasswordRequest,
    ) -> Result<()> {
        if !url_security::is_valid_salesforce_id(user_id) {
            return Err(Error::new(ErrorKind::Salesforce {
                error_code: "INVALID_ID".to_string(),
                message: "Invalid Salesforce ID format".to_string(),
            }));
        }
        let path = format!("sobjects/User/{}/password", user_id);
        let _: serde_json::Value = self.client.rest_post(&path, request).await?;
        Ok(())
    }

    /// Reset a user's password.
    ///
    /// Salesforce generates a new password and returns it in the response.
    /// This uses DELETE to trigger the reset.
    #[instrument(skip(self))]
    pub async fn reset_user_password(&self, user_id: &str) -> Result<SetPasswordResponse> {
        if !url_security::is_valid_salesforce_id(user_id) {
            return Err(Error::new(ErrorKind::Salesforce {
                error_code: "INVALID_ID".to_string(),
                message: "Invalid Salesforce ID format".to_string(),
            }));
        }
        let path = format!("sobjects/User/{}/password", user_id);
        let url = self.client.rest_url(&path);
        let request = self.client.delete(&url);
        let response = self.client.execute(request).await?;
        response.json().await.map_err(Into::into)
    }
}

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

    #[tokio::test]
    async fn test_get_user_password_status_invalid_id() {
        let client = SalesforceRestClient::new("https://test.salesforce.com", "token").unwrap();
        let result = client.get_user_password_status("bad-id").await;
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("INVALID_ID"));
    }

    #[tokio::test]
    async fn test_set_user_password_invalid_id() {
        let client = SalesforceRestClient::new("https://test.salesforce.com", "token").unwrap();
        let request = crate::user_password::SetPasswordRequest {
            new_password: "NewPass123!".to_string(),
        };
        let result = client.set_user_password("bad-id", &request).await;
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("INVALID_ID"));
    }

    #[tokio::test]
    async fn test_reset_user_password_invalid_id() {
        let client = SalesforceRestClient::new("https://test.salesforce.com", "token").unwrap();
        let result = client.reset_user_password("bad-id").await;
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("INVALID_ID"));
    }

    #[tokio::test]
    async fn test_get_user_password_status_wiremock() {
        use wiremock::matchers::{method, path_regex};
        use wiremock::{Mock, MockServer, ResponseTemplate};

        let mock_server = MockServer::start().await;

        let body = serde_json::json!({"isExpired": false});

        Mock::given(method("GET"))
            .and(path_regex(".*/sobjects/User/005xx000001Svf0AAC/password$"))
            .respond_with(ResponseTemplate::new(200).set_body_json(&body))
            .mount(&mock_server)
            .await;

        let client = SalesforceRestClient::new(mock_server.uri(), "test-token").unwrap();
        let result = client
            .get_user_password_status("005xx000001Svf0AAC")
            .await
            .expect("get_user_password_status should succeed");
        assert!(!result.is_expired);
    }

    #[tokio::test]
    async fn test_reset_user_password_wiremock() {
        use wiremock::matchers::{method, path_regex};
        use wiremock::{Mock, MockServer, ResponseTemplate};

        let mock_server = MockServer::start().await;

        let body = serde_json::json!({"NewPassword": "AutoGenerated123!"});

        Mock::given(method("DELETE"))
            .and(path_regex(".*/sobjects/User/005xx000001Svf0AAC/password$"))
            .respond_with(ResponseTemplate::new(200).set_body_json(&body))
            .mount(&mock_server)
            .await;

        let client = SalesforceRestClient::new(mock_server.uri(), "test-token").unwrap();
        let result = client
            .reset_user_password("005xx000001Svf0AAC")
            .await
            .expect("reset_user_password should succeed");
        assert_eq!(result.new_password, "AutoGenerated123!");
    }
}
