//! Resource sub-clients — hung off the main Kranth client.

use futures::Stream;

use crate::client::Kranth;
use crate::errors::Result;
use crate::sse::stream_sim_events;
use crate::types::*;

// ─── sims ──────────────────────────────────────────────────────

pub struct SimsResource<'a> {
    pub(crate) client: &'a Kranth,
}

impl<'a> SimsResource<'a> {
    pub async fn create(&self, req: &CreateSimRequest) -> Result<CreateSimResponse> {
        self.client.post("/v1/sims", req, None).await
    }

    pub async fn create_with_idempotency_key(
        &self,
        req: &CreateSimRequest,
        idempotency_key: &str,
    ) -> Result<CreateSimResponse> {
        self.client
            .post("/v1/sims", req, Some(idempotency_key))
            .await
    }

    pub async fn get(&self, sim_id: &str) -> Result<SimSummary> {
        self.client.get(&format!("/v1/sims/{sim_id}"), &[]).await
    }

    pub async fn list(&self, p: &ListSimsParams) -> Result<ListSimsResponse> {
        let mut q: Vec<(&str, String)> = Vec::new();
        if let Some(s) = &p.status {
            q.push(("status", s.clone()));
        }
        if let Some(c) = &p.cursor {
            q.push(("cursor", c.clone()));
        }
        if let Some(l) = p.limit {
            q.push(("limit", l.to_string()));
        }
        self.client.get("/v1/sims", &q).await
    }

    pub async fn cancel(&self, sim_id: &str) -> Result<serde_json::Value> {
        self.client
            .post(
                &format!("/v1/sims/{sim_id}/cancel"),
                &serde_json::json!({}),
                None,
            )
            .await
    }

    pub async fn export(&self, sim_id: &str) -> Result<serde_json::Value> {
        self.client
            .get(&format!("/v1/sims/{sim_id}/export"), &[])
            .await
    }

    /// Subscribe to the SSE event stream for a sim.  Each item is a
    /// [`Result<SimEvent>`]; the stream ends when the server closes the
    /// connection (typically after `sim.complete` or `sim.failed`).
    pub fn stream(&self, sim_id: &str) -> impl Stream<Item = Result<SimEvent>> + 'a {
        stream_sim_events(self.client.clone(), sim_id.to_string())
    }
}

// ─── api_keys ─────────────────────────────────────────────────

pub struct ApiKeysResource<'a> {
    pub(crate) client: &'a Kranth,
}

impl<'a> ApiKeysResource<'a> {
    pub async fn create(&self, name: &str, env: &str) -> Result<CreateApiKeyResponse> {
        self.client
            .post(
                "/v1/api-keys",
                &serde_json::json!({ "name": name, "env": env }),
                None,
            )
            .await
    }

    pub async fn list(&self) -> Result<Vec<ApiKey>> {
        #[derive(serde::Deserialize)]
        struct Wrap {
            keys: Vec<ApiKey>,
        }
        let w: Wrap = self.client.get("/v1/api-keys", &[]).await?;
        Ok(w.keys)
    }

    pub async fn revoke(&self, id: &str) -> Result<()> {
        self.client
            .delete_empty(&format!("/v1/api-keys/{id}"))
            .await
    }
}

// ─── models ───────────────────────────────────────────────────

pub struct ModelsResource<'a> {
    pub(crate) client: &'a Kranth,
}

impl<'a> ModelsResource<'a> {
    pub async fn list(&self) -> Result<Vec<ModelInfo>> {
        #[derive(serde::Deserialize)]
        struct Wrap {
            models: Vec<ModelInfo>,
        }
        let w: Wrap = self.client.get("/v1/models", &[]).await?;
        Ok(w.models)
    }
}
