1use crate::error::{KickApiError, Result};
2use crate::models::{
3 ChannelReward, ChannelRewardRedemption, CreateRewardRequest, ManageRedemptionsRequest,
4 ManageRedemptionsResponse, RedemptionStatus, UpdateRewardRequest,
5};
6use reqwest;
7
8pub struct RewardsApi<'a> {
10 client: &'a reqwest::Client,
11 token: &'a Option<String>,
12 base_url: &'a str,
13}
14
15impl<'a> RewardsApi<'a> {
16 pub(crate) fn new(
18 client: &'a reqwest::Client,
19 token: &'a Option<String>,
20 base_url: &'a str,
21 ) -> Self {
22 Self {
23 client,
24 token,
25 base_url,
26 }
27 }
28
29 pub async fn get_all(&self) -> Result<Vec<ChannelReward>> {
41 self.require_token()?;
42
43 let url = format!("{}/channels/rewards", self.base_url);
44 let request = self
45 .client
46 .get(&url)
47 .header("Accept", "*/*")
48 .bearer_auth(self.token.as_ref().unwrap());
49 let response = crate::http::send_with_retry(self.client, request).await?;
50
51 self.parse_response(response).await
52 }
53
54 pub async fn create(&self, request: CreateRewardRequest) -> Result<ChannelReward> {
73 self.require_token()?;
74
75 let url = format!("{}/channels/rewards", self.base_url);
76 let request = self
77 .client
78 .post(&url)
79 .header("Accept", "*/*")
80 .bearer_auth(self.token.as_ref().unwrap())
81 .json(&request);
82 let response = crate::http::send_with_retry(self.client, request).await?;
83
84 self.parse_single_response(response).await
85 }
86
87 pub async fn update(
104 &self,
105 reward_id: &str,
106 request: UpdateRewardRequest,
107 ) -> Result<ChannelReward> {
108 self.require_token()?;
109
110 let url = format!("{}/channels/rewards/{}", self.base_url, reward_id);
111 let request = self
112 .client
113 .patch(&url)
114 .header("Accept", "*/*")
115 .bearer_auth(self.token.as_ref().unwrap())
116 .json(&request);
117 let response = crate::http::send_with_retry(self.client, request).await?;
118
119 self.parse_single_response(response).await
120 }
121
122 pub async fn delete(&self, reward_id: &str) -> Result<()> {
126 self.require_token()?;
127
128 let url = format!("{}/channels/rewards/{}", self.base_url, reward_id);
129 let request = self
130 .client
131 .delete(&url)
132 .header("Accept", "*/*")
133 .bearer_auth(self.token.as_ref().unwrap());
134 let response = crate::http::send_with_retry(self.client, request).await?;
135
136 if response.status().is_success() {
137 Ok(())
138 } else {
139 Err(KickApiError::ApiError(format!(
140 "Failed to delete reward: {}",
141 response.status()
142 )))
143 }
144 }
145
146 pub async fn get_redemptions(
154 &self,
155 reward_id: Option<&str>,
156 status: Option<RedemptionStatus>,
157 ) -> Result<Vec<ChannelRewardRedemption>> {
158 self.require_token()?;
159
160 let url = format!("{}/channels/rewards/redemptions", self.base_url);
161 let mut request = self
162 .client
163 .get(&url)
164 .header("Accept", "*/*")
165 .bearer_auth(self.token.as_ref().unwrap());
166
167 if let Some(id) = reward_id {
168 request = request.query(&[("reward_id", id)]);
169 }
170
171 if let Some(s) = status {
172 let status_str = match s {
173 RedemptionStatus::Pending => "pending",
174 RedemptionStatus::Accepted => "accepted",
175 RedemptionStatus::Rejected => "rejected",
176 };
177 request = request.query(&[("status", status_str)]);
178 }
179
180 let response = crate::http::send_with_retry(self.client, request).await?;
181 self.parse_response(response).await
182 }
183
184 pub async fn accept_redemptions(
191 &self,
192 redemption_ids: Vec<String>,
193 ) -> Result<ManageRedemptionsResponse> {
194 self.manage_redemptions("accept", redemption_ids).await
195 }
196
197 pub async fn reject_redemptions(
204 &self,
205 redemption_ids: Vec<String>,
206 ) -> Result<ManageRedemptionsResponse> {
207 self.manage_redemptions("reject", redemption_ids).await
208 }
209
210 fn require_token(&self) -> Result<()> {
213 if self.token.is_none() {
214 return Err(KickApiError::ApiError(
215 "OAuth token required for this endpoint".to_string(),
216 ));
217 }
218 Ok(())
219 }
220
221 async fn parse_response<T: serde::de::DeserializeOwned>(
222 &self,
223 response: reqwest::Response,
224 ) -> Result<Vec<T>> {
225 if response.status().is_success() {
226 let body = response.text().await?;
227
228 #[derive(serde::Deserialize)]
229 struct DataResponse<T> {
230 data: Vec<T>,
231 }
232
233 let resp: DataResponse<T> = serde_json::from_str(&body)
234 .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
235
236 Ok(resp.data)
237 } else {
238 Err(KickApiError::ApiError(format!(
239 "Request failed: {}",
240 response.status()
241 )))
242 }
243 }
244
245 async fn parse_single_response<T: serde::de::DeserializeOwned>(
246 &self,
247 response: reqwest::Response,
248 ) -> Result<T> {
249 if response.status().is_success() {
250 let body = response.text().await?;
251
252 #[derive(serde::Deserialize)]
253 struct DataResponse<T> {
254 data: T,
255 }
256
257 let resp: DataResponse<T> = serde_json::from_str(&body)
258 .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
259
260 Ok(resp.data)
261 } else {
262 Err(KickApiError::ApiError(format!(
263 "Request failed: {}",
264 response.status()
265 )))
266 }
267 }
268
269 async fn manage_redemptions(
270 &self,
271 action: &str,
272 redemption_ids: Vec<String>,
273 ) -> Result<ManageRedemptionsResponse> {
274 self.require_token()?;
275
276 let url = format!("{}/channels/rewards/redemptions/{}", self.base_url, action);
277 let request_body = ManageRedemptionsRequest { ids: redemption_ids };
278
279 let request = self
280 .client
281 .post(&url)
282 .header("Accept", "*/*")
283 .bearer_auth(self.token.as_ref().unwrap())
284 .json(&request_body);
285 let response = crate::http::send_with_retry(self.client, request).await?;
286
287 if response.status().is_success() {
288 let body = response.text().await?;
289 let resp: ManageRedemptionsResponse = serde_json::from_str(&body)
290 .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
291 Ok(resp)
292 } else {
293 Err(KickApiError::ApiError(format!(
294 "Failed to {} redemptions: {}",
295 action,
296 response.status()
297 )))
298 }
299 }
300}