use crate::{
    api::{Api, Resource},
    Error, Result,
};
use k8s_openapi::api::{
    authentication::v1::TokenRequest,
    core::v1::{Node, ServiceAccount},
};
use kube_core::{params::PostParams, util::Restart};
use serde::de::DeserializeOwned;
mod csr;
impl<K> Api<K>
where
    K: Restart + Resource + DeserializeOwned,
{
    pub async fn restart(&self, name: &str) -> Result<K> {
        let mut req = self.request.restart(name).map_err(Error::BuildRequest)?;
        req.extensions_mut().insert("restart");
        self.client.request::<K>(req).await
    }
}
impl Api<Node> {
    pub async fn cordon(&self, name: &str) -> Result<Node> {
        let mut req = self.request.cordon(name).map_err(Error::BuildRequest)?;
        req.extensions_mut().insert("cordon");
        self.client.request::<Node>(req).await
    }
    pub async fn uncordon(&self, name: &str) -> Result<Node> {
        let mut req = self.request.uncordon(name).map_err(Error::BuildRequest)?;
        req.extensions_mut().insert("cordon");
        self.client.request::<Node>(req).await
    }
}
impl Api<ServiceAccount> {
    pub async fn create_token_request(
        &self,
        name: &str,
        pp: &PostParams,
        token_request: &TokenRequest,
    ) -> Result<TokenRequest> {
        let bytes = serde_json::to_vec(token_request).map_err(Error::SerdeError)?;
        let mut req = self
            .request
            .create_subresource("token", name, pp, bytes)
            .map_err(Error::BuildRequest)?;
        req.extensions_mut().insert("create_token_request");
        self.client.request::<TokenRequest>(req).await
    }
}
#[cfg(test)]
#[cfg(feature = "client")]
mod test {
    use crate::{
        api::{Api, DeleteParams, ListParams, PostParams},
        Client,
    };
    use k8s_openapi::api::{
        authentication::v1::{TokenRequest, TokenRequestSpec, TokenReview, TokenReviewSpec},
        core::v1::{Node, ServiceAccount},
    };
    use serde_json::json;
    #[tokio::test]
    #[ignore = "needs kubeconfig"]
    async fn node_cordon_and_uncordon_works() -> Result<(), Box<dyn std::error::Error>> {
        let client = Client::try_default().await?;
        let node_name = "fakenode";
        let fake_node = serde_json::from_value(json!({
        "apiVersion": "v1",
        "kind": "Node",
        "metadata": {
            "name": node_name,
            },
        }))?;
        let nodes: Api<Node> = Api::all(client.clone());
        nodes.create(&PostParams::default(), &fake_node).await?;
        let schedulables = ListParams::default().fields("spec.unschedulable==false");
        let nodes_init = nodes.list(&schedulables).await?;
        let num_nodes_before_cordon = nodes_init.items.len();
        nodes.cordon(node_name).await?;
        let nodes_after_cordon = nodes.list(&schedulables).await?;
        assert_eq!(nodes_after_cordon.items.len(), num_nodes_before_cordon - 1);
        nodes.uncordon(node_name).await?;
        let nodes_after_uncordon = nodes.list(&schedulables).await?;
        assert_eq!(nodes_after_uncordon.items.len(), num_nodes_before_cordon);
        nodes.delete(node_name, &DeleteParams::default()).await?;
        Ok(())
    }
    #[tokio::test]
    #[ignore = "requires a cluster"]
    async fn create_token_request() -> Result<(), Box<dyn std::error::Error>> {
        let client = Client::try_default().await?;
        let serviceaccount_name = "fakesa";
        let serviceaccount_namespace = "default";
        let audiences = vec!["api".to_string()];
        let serviceaccounts: Api<ServiceAccount> = Api::namespaced(client.clone(), serviceaccount_namespace);
        let tokenreviews: Api<TokenReview> = Api::all(client);
        let fake_sa = serde_json::from_value(json!({
            "apiVersion": "v1",
            "kind": "ServiceAccount",
            "metadata": {
                "name": serviceaccount_name,
            },
        }))?;
        serviceaccounts.create(&PostParams::default(), &fake_sa).await?;
        let tokenrequest = serviceaccounts
            .create_token_request(serviceaccount_name, &PostParams::default(), &TokenRequest {
                metadata: Default::default(),
                spec: TokenRequestSpec {
                    audiences: audiences.clone(),
                    bound_object_ref: None,
                    expiration_seconds: None,
                },
                status: None,
            })
            .await?;
        let token = tokenrequest.status.unwrap().token;
        assert!(!token.is_empty());
        let tokenreview = tokenreviews
            .create(&PostParams::default(), &TokenReview {
                metadata: Default::default(),
                spec: TokenReviewSpec {
                    audiences: Some(audiences.clone()),
                    token: Some(token),
                },
                status: None,
            })
            .await?;
        let tokenreviewstatus = tokenreview.status.unwrap();
        assert_eq!(tokenreviewstatus.audiences, Some(audiences));
        assert_eq!(tokenreviewstatus.authenticated, Some(true));
        assert_eq!(tokenreviewstatus.error, None);
        assert_eq!(
            tokenreviewstatus.user.unwrap().username,
            Some(format!(
                "system:serviceaccount:{serviceaccount_namespace}:{serviceaccount_name}"
            ))
        );
        serviceaccounts
            .delete(serviceaccount_name, &DeleteParams::default())
            .await?;
        Ok(())
    }
}