//! # Tensorlake Cloud SDK
//!
//! A Rust SDK for interacting with Tensorlake Cloud APIs.
//! This SDK provides a high-level, ergonomic interface for managing applications,
//! functions, and execution requests in the Tensorlake Cloud platform.
//!
//! ## Quick Start
//!
//! ```rust,no_run
//! use tensorlake::{Sdk, applications::models::ListApplicationsRequest};
//!
//! async fn example() -> Result<(), Box<dyn std::error::Error>> {
//!     // Create the SDK client
//!     let sdk = Sdk::new("https://api.tensorlake.ai", "your-api-key")?;
//!
//!     // Get the applications client
//!     let apps_client = sdk.applications();
//!
//!     // List applications in the default namespace
//!     let request = ListApplicationsRequest::builder()
//!         .namespace("default".to_string())
//!         .build()?;
//!     apps_client.list(&request).await?;
//!     Ok(())
//! }
//! ```
//!
//! ## Authentication
//!
//! The SDK uses Bearer token authentication, either a Personal Access Token (PAT) or a Project API key.
//! Provide your token when creating the SDK:
//!
//! ```rust,no_run
//! use tensorlake::Sdk;
//!
//! let sdk = Sdk::new("https://api.tensorlake.ai", "your-token").unwrap();
//! ```
//!
//! ## Available Clients
//!
//! - [`ApplicationsClient`](applications::ApplicationsClient): Manage applications, functions, and requests
//! - [`ImagesClient`](images::ImagesClient): Build and manage container images
//! - [`SandboxesClient`](sandboxes::SandboxesClient): Manage sandbox lifecycle, pools, and snapshots
//! - [`SecretsClient`](secrets::SecretsClient): Manage secrets for secure configuration
//! - [`DocumentAiClient`](document_ai::DocumentAiClient): Manage Document AI APIs
//!
//! ## Error Handling
//!
//! The SDK provides detailed error types for different scenarios:
//!
//! ```rust,no_run
//! use tensorlake::{Sdk, applications::models::ListApplicationsRequest};
//!
//! async fn example() -> Result<(), Box<dyn std::error::Error>> {
//!     let sdk = Sdk::new("https://api.tensorlake.ai", "your-api-key")?;
//!     let apps_client = sdk.applications();
//!
//!     let request = ListApplicationsRequest::builder()
//!         .namespace("default".to_string())
//!         .build()?;
//!     match apps_client.list(&request).await {
//!         Ok(apps) => println!("Success: {:?}", apps.applications.len()),
//!         Err(e) => eprintln!("Error: {}", e),
//!     }
//!     Ok(())
//! }
//! ```

pub mod applications;
pub mod cron;
pub mod document_ai;
pub mod error;
pub mod images;
pub mod sandbox_images;
pub mod sandbox_templates;
pub mod sandboxes;
pub mod secrets;
use applications::*;
use cron::*;
use document_ai::*;
use images::*;
use sandbox_templates::*;
use sandboxes::*;
use secrets::*;

mod client;
pub use client::{Client, ClientBuilder, Traced};

/// Derive the sandbox lifecycle base URL from the API URL.
///
/// Converts `api.tensorlake.*` → `sandbox.tensorlake.*`. Localhost is unchanged.
pub fn resolve_sandbox_lifecycle_url(api_url: &str) -> String {
    if let Ok(parsed) = url::Url::parse(api_url) {
        let host = parsed.host_str().unwrap_or("");
        if host == "localhost" || host == "127.0.0.1" {
            return api_url.to_string();
        }
        if let Some(rest) = host.strip_prefix("api.") {
            return format!("{}://sandbox.{}", parsed.scheme(), rest);
        }
    }
    "https://sandbox.tensorlake.ai".to_string()
}

/// The main entry point for the Tensorlake Cloud SDK.
///
/// The `Sdk` struct provides a unified interface to all Tensorlake Cloud services.
/// It manages authentication and provides access to various service clients.
///
/// ## Example
///
/// ```rust
/// use tensorlake::Sdk;
///
/// let sdk = Sdk::new("https://api.tensorlake.ai", "your-api-key").unwrap();
///
/// // Access different service clients
/// let apps_client = sdk.applications();
/// let secrets_client = sdk.secrets();
/// ```
#[derive(Clone)]
pub struct Sdk {
    client: Client,
    api_url: String,
}

impl Sdk {
    /// Create a new SDK instance with the specified base URL and bearer token.
    ///
    /// # Arguments
    ///
    /// * `base_url` - The base URL of the Tensorlake Cloud API (e.g., "https://api.tensorlake.ai")
    /// * `bearer_token` - Your API key for authentication
    ///
    /// # Returns
    ///
    /// Returns a new `Sdk` instance configured with the provided credentials.
    ///
    /// # Errors
    ///
    /// Returns an error if the HTTP client cannot be created or configured.
    ///
    /// # Example
    ///
    /// ```rust
    /// use tensorlake::Sdk;
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let sdk = Sdk::new("https://api.tensorlake.ai", "your-api-key").unwrap();
    /// Ok(())
    /// # }
    /// ```
    pub fn new(base_url: &str, bearer_token: &str) -> Result<Self, error::SdkError> {
        let client = ClientBuilder::new(base_url)
            .bearer_token(bearer_token)
            .build()?;
        Ok(Self {
            client,
            api_url: base_url.to_string(),
        })
    }

    /// Create a new SDK instance using a client builder.
    ///
    /// This method allows for more flexible configuration of the SDK client,
    /// including custom middleware, bearer tokens, and organization/project scopes.
    ///
    /// # Arguments
    ///
    /// * `builder` - A configured [`ClientBuilder`]
    ///
    /// # Returns
    ///
    /// Returns a new `Sdk` instance configured with the builder's settings.
    ///
    /// # Errors
    ///
    /// Returns an error if the HTTP client cannot be created or configured.
    ///
    /// # Example
    ///
    /// ```rust
    /// use tensorlake::{Sdk, ClientBuilder};
    ///
    /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
    /// let builder = ClientBuilder::new("https://api.tensorlake.ai")
    ///     .bearer_token("your-api-key")
    ///     .scope("org-id", "project-id");
    /// let sdk = Sdk::with_client_builder(builder)?;
    /// Ok(())
    /// # }
    /// ```
    pub fn with_client_builder(builder: ClientBuilder) -> Result<Self, error::SdkError> {
        let api_url = builder.base_url().to_string();
        let client = builder.build()?;
        Ok(Self { client, api_url })
    }

    /// Get a client for managing applications and requests.
    ///
    /// This method returns an [`ApplicationsClient`] that provides methods for:
    /// - Listing, creating, updating, and deleting applications
    /// - Invoking applications with data
    /// - Managing execution requests
    ///
    /// # Returns
    ///
    /// Returns an [`ApplicationsClient`] instance configured with the SDK's authentication.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use tensorlake::{Sdk, applications::models::ListApplicationsRequest};
    ///
    /// async fn example() -> Result<(), Box<dyn std::error::Error>> {
    ///     let sdk = Sdk::new("https://api.tensorlake.ai", "your-api-key")?;
    ///     let apps_client = sdk.applications();
    ///
    ///     // Use the applications client
    ///     let request = ListApplicationsRequest::builder()
    ///         .namespace("default".to_string())
    ///         .build()?;
    ///     apps_client.list(&request).await?;
    ///     Ok(())
    /// }
    /// ```
    pub fn applications(&self) -> ApplicationsClient {
        ApplicationsClient::new(self.client.clone())
    }

    /// Get a client for building and managing container images.
    ///
    /// This method returns an [`ImagesClient`] that provides methods for:
    /// - Building container images from source code and Dockerfiles
    /// - Monitoring build progress and status
    ///
    /// # Returns
    ///
    /// Returns an [`ImagesClient`] instance configured with the SDK's authentication.
    ///
    /// # Example
    ///
    /// ```rust
    /// use tensorlake::Sdk;
    ///
    /// let sdk = Sdk::new("https://api.tensorlake.ai", "your-api-key").unwrap();
    /// let images_client = sdk.images();
    ///
    /// // Use the images client
    /// // let result = images_client.build_image(request).await?;
    /// ```
    pub fn images(&self) -> ImagesClient {
        ImagesClient::new(self.client.clone())
    }

    /// Get a client for managing snapshot-backed sandbox image registrations.
    pub fn sandbox_templates(
        &self,
        organization_id: &str,
        project_id: &str,
    ) -> SandboxTemplatesClient {
        SandboxTemplatesClient::new(
            self.client.clone(),
            organization_id.to_string(),
            project_id.to_string(),
        )
    }

    /// Get a client for managing sandbox lifecycle, pools, and snapshots.
    ///
    /// # Arguments
    ///
    /// * `namespace` - Namespace used when `use_namespaced_endpoints` is true
    /// * `use_namespaced_endpoints` - If true, use `/v1/namespaces/{namespace}/...`
    ///   paths. If false, use cloud-style top-level paths such as `/sandboxes`.
    pub fn sandboxes(&self, namespace: &str, use_namespaced_endpoints: bool) -> SandboxesClient {
        let lifecycle_client = self
            .client
            .with_base_url(&resolve_sandbox_lifecycle_url(&self.api_url));
        SandboxesClient::new(
            lifecycle_client,
            namespace.to_string(),
            use_namespaced_endpoints,
        )
    }

    /// Get a client for Document AI APIs.
    pub fn document_ai(&self) -> DocumentAiClient {
        DocumentAiClient::new(self.client.clone())
    }

    /// Get a client for managing secrets.
    ///
    /// This method returns a [`SecretsClient`] that provides methods for:
    /// - Creating, updating, and deleting secrets
    /// - Listing secrets in a project
    /// - Retrieving individual secret details
    ///
    /// # Returns
    ///
    /// Returns a [`SecretsClient`] instance configured with the SDK's authentication.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use tensorlake::{Sdk, secrets::models::ListSecretsRequest};
    ///
    /// async fn example() -> Result<(), Box<dyn std::error::Error>> {
    ///     let sdk = Sdk::new("https://api.tensorlake.ai", "your-api-key")?;
    ///     let secrets_client = sdk.secrets();
    ///
    ///     // Use the secrets client
    ///     let request = ListSecretsRequest::builder()
    ///         .organization_id("org-id".to_string())
    ///         .project_id("project-id".to_string())
    ///         .build()?;
    ///     secrets_client.list(&request).await?;
    ///     Ok(())
    /// }
    /// ```
    pub fn secrets(&self) -> SecretsClient {
        SecretsClient::new(self.client.clone())
    }

    /// Get a client for managing cron schedules for applications.
    pub fn cron(&self) -> CronClient {
        CronClient::new(self.client.clone())
    }
}
