// Code generated by TofuPilot SDK Generator. DO NOT EDIT.

use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;

/// Context passed to hooks.
///
/// Uses `Arc<str>` internally so cloning is cheap (pointer copy, not allocation).
#[derive(Debug, Clone)]
pub struct HookContext {
    /// The operation ID (e.g. "run-create")
    pub operation_id: Arc<str>,
    /// Base URL being used
    pub base_url: Arc<str>,
}

/// A hook function that can inspect/modify requests or responses.
pub type Hook = Arc<
    dyn Fn(HookContext, reqwest::Request) -> Pin<Box<dyn Future<Output = reqwest::Request> + Send>>
        + Send
        + Sync,
>;

/// After-response hook that can inspect the response.
pub type AfterHook = Arc<
    dyn Fn(HookContext, &reqwest::Response) -> Pin<Box<dyn Future<Output = ()> + Send>>
        + Send
        + Sync,
>;

/// After-error hook that can inspect errors.
pub type ErrorHook = Arc<
    dyn Fn(HookContext, &crate::Error) -> Pin<Box<dyn Future<Output = ()> + Send>>
        + Send
        + Sync,
>;

/// Collection of hooks for request lifecycle.
#[derive(Clone, Default)]
pub struct Hooks {
    /// Called before each request is sent. Can modify the request.
    pub before_request: Vec<Hook>,
    /// Called after a successful response.
    pub after_success: Vec<AfterHook>,
    /// Called after an error occurs.
    pub after_error: Vec<ErrorHook>,
}

impl std::fmt::Debug for Hooks {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Hooks")
            .field("before_request", &self.before_request.len())
            .field("after_success", &self.after_success.len())
            .field("after_error", &self.after_error.len())
            .finish()
    }
}

impl Hooks {
    /// Create an empty hook set.
    pub fn new() -> Self {
        Self::default()
    }

    /// Add a before-request hook.
    pub fn on_before_request<F, Fut>(mut self, f: F) -> Self
    where
        F: Fn(HookContext, reqwest::Request) -> Fut + Send + Sync + 'static,
        Fut: Future<Output = reqwest::Request> + Send + 'static,
    {
        self.before_request.push(Arc::new(move |ctx, req| {
            Box::pin(f(ctx, req))
        }));
        self
    }

    /// Add an after-success hook.
    pub fn on_after_success<F, Fut>(mut self, f: F) -> Self
    where
        F: Fn(HookContext, &reqwest::Response) -> Fut + Send + Sync + 'static,
        Fut: Future<Output = ()> + Send + 'static,
    {
        self.after_success.push(Arc::new(move |ctx, resp| {
            Box::pin(f(ctx, resp))
        }));
        self
    }

    /// Add an after-error hook.
    pub fn on_after_error<F, Fut>(mut self, f: F) -> Self
    where
        F: Fn(HookContext, &crate::Error) -> Fut + Send + Sync + 'static,
        Fut: Future<Output = ()> + Send + 'static,
    {
        self.after_error.push(Arc::new(move |ctx, err| {
            Box::pin(f(ctx, err))
        }));
        self
    }

    pub(crate) async fn run_before(&self, ctx: &HookContext, mut request: reqwest::Request) -> reqwest::Request {
        for hook in &self.before_request {
            request = hook(ctx.clone(), request).await;
        }
        request
    }

    pub(crate) async fn run_after_success(&self, ctx: &HookContext, response: &reqwest::Response) {
        for hook in &self.after_success {
            hook(ctx.clone(), response).await;
        }
    }

    pub(crate) async fn run_after_error(&self, ctx: &HookContext, error: &crate::Error) {
        for hook in &self.after_error {
            hook(ctx.clone(), error).await;
        }
    }
}
