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 super::require_token(self.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 super::require_token(self.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 super::require_token(self.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 super::require_token(self.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 super::require_token(self.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 async fn parse_response<T: serde::de::DeserializeOwned>(
213 &self,
214 response: reqwest::Response,
215 ) -> Result<Vec<T>> {
216 if response.status().is_success() {
217 let body = response.text().await?;
218
219 #[derive(serde::Deserialize)]
220 struct DataResponse<T> {
221 data: Vec<T>,
222 }
223
224 let resp: DataResponse<T> = serde_json::from_str(&body)
225 .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
226
227 Ok(resp.data)
228 } else {
229 Err(KickApiError::ApiError(format!(
230 "Request failed: {}",
231 response.status()
232 )))
233 }
234 }
235
236 async fn parse_single_response<T: serde::de::DeserializeOwned>(
237 &self,
238 response: reqwest::Response,
239 ) -> Result<T> {
240 if response.status().is_success() {
241 let body = response.text().await?;
242
243 #[derive(serde::Deserialize)]
244 struct DataResponse<T> {
245 data: T,
246 }
247
248 let resp: DataResponse<T> = serde_json::from_str(&body)
249 .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
250
251 Ok(resp.data)
252 } else {
253 Err(KickApiError::ApiError(format!(
254 "Request failed: {}",
255 response.status()
256 )))
257 }
258 }
259
260 async fn manage_redemptions(
261 &self,
262 action: &str,
263 redemption_ids: Vec<String>,
264 ) -> Result<ManageRedemptionsResponse> {
265 super::require_token(self.token)?;
266
267 let url = format!("{}/channels/rewards/redemptions/{}", self.base_url, action);
268 let request_body = ManageRedemptionsRequest { ids: redemption_ids };
269
270 let request = self
271 .client
272 .post(&url)
273 .header("Accept", "*/*")
274 .bearer_auth(self.token.as_ref().unwrap())
275 .json(&request_body);
276 let response = crate::http::send_with_retry(self.client, request).await?;
277
278 if response.status().is_success() {
279 let body = response.text().await?;
280 let resp: ManageRedemptionsResponse = serde_json::from_str(&body)
281 .map_err(|e| KickApiError::ApiError(format!("JSON parse error: {}", e)))?;
282 Ok(resp)
283 } else {
284 Err(KickApiError::ApiError(format!(
285 "Failed to {} redemptions: {}",
286 action,
287 response.status()
288 )))
289 }
290 }
291}