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

use crate::client::TofuPilot;
use crate::error::{Error, Result};
use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC};

/// Percent-encode set matching RFC 3986 unreserved characters.
const URI_COMPONENT: &AsciiSet = &NON_ALPHANUMERIC
    .remove(b'-')
    .remove(b'.')
    .remove(b'_')
    .remove(b'~');
use crate::types::*;

/// Client for the Runs API.
#[derive(Debug, Clone)]
pub struct RunsClient<'a> {
    pub(crate) client: &'a TofuPilot,
}

impl<'a> RunsClient<'a> {
    pub(crate) fn new(client: &'a TofuPilot) -> Self {
        Self { client }
    }

    /// List and filter runs
    ///
    /// Retrieve a paginated list of test runs with filtering by unit, procedure, date range, outcome, and station.
    pub fn list(&self) -> ListBuilder<'a> {
        ListBuilder::new(self.client)
    }

    /// Create run
    ///
    /// Create a new test run, linking it to a procedure and unit. Existing entities are reused automatically.
    pub fn create(&self) -> CreateBuilder<'a> {
        CreateBuilder::new(self.client)
    }

    /// Delete runs
    ///
    /// Permanently delete test runs by their IDs. Removes all associated phases, measurements, and attachments.
    pub fn delete(&self) -> DeleteBuilder<'a> {
        DeleteBuilder::new(self.client)
    }

    /// Get run
    ///
    /// Retrieve a single test run by its ID. Returns comprehensive run data including metadata, phases, measurements, and logs.
    pub fn get(&self) -> GetBuilder<'a> {
        GetBuilder::new(self.client)
    }

    /// Update run
    ///
    /// Update a test run, including linking file attachments. Files must be uploaded via Initialize upload and Finalize upload before linking.
    pub fn update(&self) -> UpdateBuilder<'a> {
        UpdateBuilder::new(self.client)
    }

    /// Attach file to run
    ///
    /// Create an attachment linked to a run and get a temporary pre-signed URL. Upload the file to the URL with a PUT request to complete the attachment.
    pub fn create_attachment(&self) -> CreateAttachmentBuilder<'a> {
        CreateAttachmentBuilder::new(self.client)
    }

}

// ---------------------------------------------------------------------------
// ListBuilder
// ---------------------------------------------------------------------------

/// Builder for [`RunsClient::list`].
///
/// # Example
///
/// ```no_run
/// use tofupilot::TofuPilot;
///
/// # #[tokio::main]
/// # async fn main() -> tofupilot::Result<()> {
/// let client = TofuPilot::new("your-api-key");
/// let response = client.runs().list()
///     .send()
///     .await?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct ListBuilder<'a> {
    client: &'a TofuPilot,
    search_query: Option<String>,
    ids: Option<Vec<String>>,
    outcomes: Option<Vec<Outcome>>,
    procedure_ids: Option<Vec<String>>,
    procedure_versions: Option<Vec<String>>,
    serial_numbers: Option<Vec<String>>,
    part_numbers: Option<Vec<String>>,
    revision_numbers: Option<Vec<String>>,
    batch_numbers: Option<Vec<String>>,
    duration_min: Option<String>,
    duration_max: Option<String>,
    started_after: Option<chrono::DateTime<chrono::Utc>>,
    started_before: Option<chrono::DateTime<chrono::Utc>>,
    ended_after: Option<chrono::DateTime<chrono::Utc>>,
    ended_before: Option<chrono::DateTime<chrono::Utc>>,
    created_after: Option<chrono::DateTime<chrono::Utc>>,
    created_before: Option<chrono::DateTime<chrono::Utc>>,
    created_by_user_ids: Option<Vec<String>>,
    created_by_station_ids: Option<Vec<String>>,
    operated_by_ids: Option<Vec<String>>,
    limit: Option<i64>,
    cursor: Option<i64>,
    sort_by: Option<RunListSortBy>,
    sort_order: Option<ListSortOrder>,
    server_url: Option<String>,
    timeout: Option<std::time::Duration>,
}

impl<'a> ListBuilder<'a> {
    pub(crate) fn new(client: &'a TofuPilot) -> Self {
        Self {
            client,
            search_query: None,
            ids: None,
            outcomes: None,
            procedure_ids: None,
            procedure_versions: None,
            serial_numbers: None,
            part_numbers: None,
            revision_numbers: None,
            batch_numbers: None,
            duration_min: None,
            duration_max: None,
            started_after: None,
            started_before: None,
            ended_after: None,
            ended_before: None,
            created_after: None,
            created_before: None,
            created_by_user_ids: None,
            created_by_station_ids: None,
            operated_by_ids: None,
            limit: None,
            cursor: None,
            sort_by: None,
            sort_order: None,
            server_url: None,
            timeout: None,
        }
    }

    /// Set the `search_query` query parameter.
    pub fn search_query(mut self, value: impl Into<String>) -> Self {
        self.search_query = Some(value.into());
        self
    }

    /// Set the `ids` query parameter.
    pub fn ids(mut self, value: impl Into<Vec<String>>) -> Self {
        self.ids = Some(value.into());
        self
    }

    /// Set the `outcomes` query parameter.
    pub fn outcomes(mut self, value: impl Into<Vec<Outcome>>) -> Self {
        self.outcomes = Some(value.into());
        self
    }

    /// Set the `procedure_ids` query parameter.
    pub fn procedure_ids(mut self, value: impl Into<Vec<String>>) -> Self {
        self.procedure_ids = Some(value.into());
        self
    }

    /// Set the `procedure_versions` query parameter.
    pub fn procedure_versions(mut self, value: impl Into<Vec<String>>) -> Self {
        self.procedure_versions = Some(value.into());
        self
    }

    /// Set the `serial_numbers` query parameter.
    pub fn serial_numbers(mut self, value: impl Into<Vec<String>>) -> Self {
        self.serial_numbers = Some(value.into());
        self
    }

    /// Set the `part_numbers` query parameter.
    pub fn part_numbers(mut self, value: impl Into<Vec<String>>) -> Self {
        self.part_numbers = Some(value.into());
        self
    }

    /// Set the `revision_numbers` query parameter.
    pub fn revision_numbers(mut self, value: impl Into<Vec<String>>) -> Self {
        self.revision_numbers = Some(value.into());
        self
    }

    /// Set the `batch_numbers` query parameter.
    pub fn batch_numbers(mut self, value: impl Into<Vec<String>>) -> Self {
        self.batch_numbers = Some(value.into());
        self
    }

    /// Set the `duration_min` query parameter.
    pub fn duration_min(mut self, value: impl Into<String>) -> Self {
        self.duration_min = Some(value.into());
        self
    }

    /// Set the `duration_max` query parameter.
    pub fn duration_max(mut self, value: impl Into<String>) -> Self {
        self.duration_max = Some(value.into());
        self
    }

    /// Set the `started_after` query parameter.
    pub fn started_after(mut self, value: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
        self.started_after = Some(value.into());
        self
    }

    /// Set the `started_before` query parameter.
    pub fn started_before(mut self, value: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
        self.started_before = Some(value.into());
        self
    }

    /// Set the `ended_after` query parameter.
    pub fn ended_after(mut self, value: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
        self.ended_after = Some(value.into());
        self
    }

    /// Set the `ended_before` query parameter.
    pub fn ended_before(mut self, value: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
        self.ended_before = Some(value.into());
        self
    }

    /// Set the `created_after` query parameter.
    pub fn created_after(mut self, value: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
        self.created_after = Some(value.into());
        self
    }

    /// Set the `created_before` query parameter.
    pub fn created_before(mut self, value: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
        self.created_before = Some(value.into());
        self
    }

    /// Set the `created_by_user_ids` query parameter.
    pub fn created_by_user_ids(mut self, value: impl Into<Vec<String>>) -> Self {
        self.created_by_user_ids = Some(value.into());
        self
    }

    /// Set the `created_by_station_ids` query parameter.
    pub fn created_by_station_ids(mut self, value: impl Into<Vec<String>>) -> Self {
        self.created_by_station_ids = Some(value.into());
        self
    }

    /// Set the `operated_by_ids` query parameter.
    pub fn operated_by_ids(mut self, value: impl Into<Vec<String>>) -> Self {
        self.operated_by_ids = Some(value.into());
        self
    }

    /// Set the `limit` query parameter.
    ///
    /// Maximum number of runs to return per page.
    pub fn limit(mut self, value: impl Into<i64>) -> Self {
        self.limit = Some(value.into());
        self
    }

    /// Set the `cursor` query parameter.
    pub fn cursor(mut self, value: impl Into<i64>) -> Self {
        self.cursor = Some(value.into());
        self
    }

    /// Set the `sort_by` query parameter.
    ///
    /// Field to sort results by.
    pub fn sort_by(mut self, value: impl Into<RunListSortBy>) -> Self {
        self.sort_by = Some(value.into());
        self
    }

    /// Set the `sort_order` query parameter.
    ///
    /// Sort order direction.
    pub fn sort_order(mut self, value: impl Into<ListSortOrder>) -> Self {
        self.sort_order = Some(value.into());
        self
    }

    /// Override the server URL for this request.
    pub fn server_url(mut self, url: impl Into<String>) -> Self {
        self.server_url = Some(url.into());
        self
    }

    /// Override the timeout for this request.
    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    /// Send the request.
    pub async fn send(self) -> Result<RunListResponse> {

        let base_url = self.server_url
            .as_deref()
            .unwrap_or(&self.client.config.base_url);

        let url = format!("{}{}", base_url, "/v2/runs");

        let mut request = self.client.http.request(
            reqwest::Method::GET,
            &url,
        );

        if let Some(timeout) = self.timeout {
            request = request.timeout(timeout);
        }

        if let Some(ref val) = self.search_query {
            request = request.query(&[("search_query", val.to_string())]);
        }
        if let Some(ref val) = self.ids {
            for item in val {
                request = request.query(&[("ids", item.to_string())]);
            }
        }
        if let Some(ref val) = self.outcomes {
            for item in val {
                request = request.query(&[("outcomes", item.to_string())]);
            }
        }
        if let Some(ref val) = self.procedure_ids {
            for item in val {
                request = request.query(&[("procedure_ids", item.to_string())]);
            }
        }
        if let Some(ref val) = self.procedure_versions {
            for item in val {
                request = request.query(&[("procedure_versions", item.to_string())]);
            }
        }
        if let Some(ref val) = self.serial_numbers {
            for item in val {
                request = request.query(&[("serial_numbers", item.to_string())]);
            }
        }
        if let Some(ref val) = self.part_numbers {
            for item in val {
                request = request.query(&[("part_numbers", item.to_string())]);
            }
        }
        if let Some(ref val) = self.revision_numbers {
            for item in val {
                request = request.query(&[("revision_numbers", item.to_string())]);
            }
        }
        if let Some(ref val) = self.batch_numbers {
            for item in val {
                request = request.query(&[("batch_numbers", item.to_string())]);
            }
        }
        if let Some(ref val) = self.duration_min {
            request = request.query(&[("duration_min", val.to_string())]);
        }
        if let Some(ref val) = self.duration_max {
            request = request.query(&[("duration_max", val.to_string())]);
        }
        if let Some(ref val) = self.started_after {
            request = request.query(&[("started_after", val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true))]);
        }
        if let Some(ref val) = self.started_before {
            request = request.query(&[("started_before", val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true))]);
        }
        if let Some(ref val) = self.ended_after {
            request = request.query(&[("ended_after", val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true))]);
        }
        if let Some(ref val) = self.ended_before {
            request = request.query(&[("ended_before", val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true))]);
        }
        if let Some(ref val) = self.created_after {
            request = request.query(&[("created_after", val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true))]);
        }
        if let Some(ref val) = self.created_before {
            request = request.query(&[("created_before", val.to_rfc3339_opts(chrono::SecondsFormat::Millis, true))]);
        }
        if let Some(ref val) = self.created_by_user_ids {
            for item in val {
                request = request.query(&[("created_by_user_ids", item.to_string())]);
            }
        }
        if let Some(ref val) = self.created_by_station_ids {
            for item in val {
                request = request.query(&[("created_by_station_ids", item.to_string())]);
            }
        }
        if let Some(ref val) = self.operated_by_ids {
            for item in val {
                request = request.query(&[("operated_by_ids", item.to_string())]);
            }
        }
        if let Some(ref val) = self.limit {
            request = request.query(&[("limit", val.to_string())]);
        }
        if let Some(ref val) = self.cursor {
            request = request.query(&[("cursor", val.to_string())]);
        }
        if let Some(ref val) = self.sort_by {
            request = request.query(&[("sort_by", val.to_string())]);
        }
        if let Some(ref val) = self.sort_order {
            request = request.query(&[("sort_order", val.to_string())]);
        }


        let response = self.client.execute(
            request,
            "run-list",
            base_url,
        ).await?;
        let result: RunListResponse = response.json().await?;
        Ok(result)
    }
}

// ---------------------------------------------------------------------------
// CreateBuilder
// ---------------------------------------------------------------------------

/// Builder for [`RunsClient::create`].
///
/// # Example
///
/// ```no_run
/// use tofupilot::TofuPilot;
///
/// # #[tokio::main]
/// # async fn main() -> tofupilot::Result<()> {
/// let client = TofuPilot::new("your-api-key");
/// let response = client.runs().create()
///     .outcome(tofupilot::types::Outcome::Pass)
///     .procedure_id("value")
///     .started_at(chrono::Utc::now())
///     .ended_at(chrono::Utc::now())
///     .serial_number("value")
///     .send()
///     .await?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct CreateBuilder<'a> {
    client: &'a TofuPilot,
    outcome: Option<Outcome>,
    procedure_id: Option<String>,
    deployment_id: Option<NullableField<String>>,
    procedure_version: Option<NullableField<String>>,
    operated_by: Option<String>,
    started_at: Option<chrono::DateTime<chrono::Utc>>,
    ended_at: Option<chrono::DateTime<chrono::Utc>>,
    serial_number: Option<String>,
    part_number: Option<String>,
    revision_number: Option<String>,
    batch_number: Option<String>,
    sub_units: Option<Vec<String>>,
    docstring: Option<String>,
    phases: Option<Vec<RunCreatePhases>>,
    logs: Option<Vec<RunCreateLogs>>,
    server_url: Option<String>,
    timeout: Option<std::time::Duration>,
}

impl<'a> CreateBuilder<'a> {
    pub(crate) fn new(client: &'a TofuPilot) -> Self {
        Self {
            client,
            outcome: None,
            procedure_id: None,
            deployment_id: None,
            procedure_version: None,
            operated_by: None,
            started_at: None,
            ended_at: None,
            serial_number: None,
            part_number: None,
            revision_number: None,
            batch_number: None,
            sub_units: None,
            docstring: None,
            phases: None,
            logs: None,
            server_url: None,
            timeout: None,
        }
    }

    /// Set the `outcome` field.
    ///
    /// Overall test result. Use PASS when test succeeds, FAIL when test fails but script execution completed successfully, ERROR when script execution fails, TIMEOUT when test exceeds time limit, ABORTED for manual script interruption.
    pub fn outcome(mut self, value: impl Into<Outcome>) -> Self {
        self.outcome = Some(value.into());
        self
    }

    /// Set the `procedure_id` field.
    ///
    /// Procedure ID. Create the procedure in the app first, then find the auto-generated ID on the procedure page.
    pub fn procedure_id(mut self, value: impl Into<String>) -> Self {
        self.procedure_id = Some(value.into());
        self
    }

    /// Set the `deployment_id` field.
    pub fn deployment_id(mut self, value: impl Into<String>) -> Self {
        self.deployment_id = Some(NullableField::Value(value.into()));
        self
    }

    /// Explicitly set `deployment_id` to null.
    pub fn deployment_id_null(mut self) -> Self {
        self.deployment_id = Some(NullableField::Null);
        self
    }

    /// Set the `procedure_version` field.
    pub fn procedure_version(mut self, value: impl Into<String>) -> Self {
        self.procedure_version = Some(NullableField::Value(value.into()));
        self
    }

    /// Explicitly set `procedure_version` to null.
    pub fn procedure_version_null(mut self) -> Self {
        self.procedure_version = Some(NullableField::Null);
        self
    }

    /// Set the `operated_by` field.
    ///
    /// Email address of the operator who executed the test run. The operator must exist as a user in the system. The run will be linked to this user to track who performed the test.
    pub fn operated_by(mut self, value: impl Into<String>) -> Self {
        self.operated_by = Some(value.into());
        self
    }

    /// Set the `started_at` field.
    ///
    /// ISO 8601 timestamp when the test run began execution. This timestamp will be used to track when the test execution started and for historical analysis of test runs. A separate created_at timestamp is stored internally server side to track upload date.
    pub fn started_at(mut self, value: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
        self.started_at = Some(value.into());
        self
    }

    /// Set the `ended_at` field.
    ///
    /// ISO 8601 timestamp when the test run finished execution.
    pub fn ended_at(mut self, value: impl Into<chrono::DateTime<chrono::Utc>>) -> Self {
        self.ended_at = Some(value.into());
        self
    }

    /// Set the `serial_number` field.
    ///
    /// Unique serial number of the unit under test. Matched case-insensitively. If no unit with this serial number exists, one will be created.
    pub fn serial_number(mut self, value: impl Into<String>) -> Self {
        self.serial_number = Some(value.into());
        self
    }

    /// Set the `part_number` field.
    ///
    /// Component part number for the unit. Matched case-insensitively. This field is required if the part number cannot be extracted from the serial number (as set in the settings). This field takes precedence over extraction from serial number. A component with the provided or extracted part number will be created if one does not exist.
    pub fn part_number(mut self, value: impl Into<String>) -> Self {
        self.part_number = Some(value.into());
        self
    }

    /// Set the `revision_number` field.
    ///
    /// Hardware revision identifier for the unit. Matched case-insensitively. If none exist, a revision with this number will be created. If no revision is specified, the unit will be linked to the default revision of the part number.
    pub fn revision_number(mut self, value: impl Into<String>) -> Self {
        self.revision_number = Some(value.into());
        self
    }

    /// Set the `batch_number` field.
    ///
    /// Production batch identifier for grouping units manufactured together. Matched case-insensitively. If none exist, a batch with this batch number will be created. If no batch number is specified, the unit will not be linked to any batch.
    pub fn batch_number(mut self, value: impl Into<String>) -> Self {
        self.batch_number = Some(value.into());
        self
    }

    /// Set the `sub_units` field.
    ///
    /// Array of sub-unit serial numbers that are part of this main unit. Matched case-insensitively. Each sub-unit must already exist and will be linked as a sub-component of the main unit under test. If no sub-units are specified, the unit will be created without sub-unit relationships.
    pub fn sub_units(mut self, value: impl Into<Vec<String>>) -> Self {
        self.sub_units = Some(value.into());
        self
    }

    /// Set the `docstring` field.
    ///
    /// Additional notes or documentation about this test run.
    pub fn docstring(mut self, value: impl Into<String>) -> Self {
        self.docstring = Some(value.into());
        self
    }

    /// Set the `phases` field.
    ///
    /// Array of test phases with measurements and results. Each phase represents a distinct stage of the test execution with timing information, outcome status, and optional measurements. If no phases are specified, the run will be created without phase-level organization of test data.
    pub fn phases(mut self, value: impl Into<Vec<RunCreatePhases>>) -> Self {
        self.phases = Some(value.into());
        self
    }

    /// Set the `logs` field.
    ///
    /// Array of log messages generated during the test execution. Each log entry captures events, errors, and diagnostic information with severity levels and source code references. If no logs are specified, the run will be created without log entries.
    pub fn logs(mut self, value: impl Into<Vec<RunCreateLogs>>) -> Self {
        self.logs = Some(value.into());
        self
    }

    /// Set the full request body (alternative to setting individual fields).
    pub fn body(mut self, body: RunCreateRequest) -> Self {
        self.outcome = Some(body.outcome);
        self.procedure_id = Some(body.procedure_id);
        self.deployment_id = Some(body.deployment_id);
        self.procedure_version = Some(body.procedure_version);
        if body.operated_by.is_some() {
            self.operated_by = body.operated_by;
        }
        self.started_at = Some(body.started_at);
        self.ended_at = Some(body.ended_at);
        self.serial_number = Some(body.serial_number);
        if body.part_number.is_some() {
            self.part_number = body.part_number;
        }
        if body.revision_number.is_some() {
            self.revision_number = body.revision_number;
        }
        if body.batch_number.is_some() {
            self.batch_number = body.batch_number;
        }
        if body.sub_units.is_some() {
            self.sub_units = body.sub_units;
        }
        if body.docstring.is_some() {
            self.docstring = body.docstring;
        }
        if body.phases.is_some() {
            self.phases = body.phases;
        }
        if body.logs.is_some() {
            self.logs = body.logs;
        }
        self
    }

    /// Override the server URL for this request.
    pub fn server_url(mut self, url: impl Into<String>) -> Self {
        self.server_url = Some(url.into());
        self
    }

    /// Override the timeout for this request.
    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    /// Send the request.
    pub async fn send(self) -> Result<RunCreateResponse> {

        let base_url = self.server_url
            .as_deref()
            .unwrap_or(&self.client.config.base_url);

        let url = format!("{}{}", base_url, "/v2/runs");

        let mut request = self.client.http.request(
            reqwest::Method::POST,
            &url,
        );

        if let Some(timeout) = self.timeout {
            request = request.timeout(timeout);
        }


        let body = RunCreateRequest {
            outcome: self.outcome
                .ok_or_else(|| Error::Validation(
                    "missing required field: outcome".to_string(),
                ))?,
            procedure_id: self.procedure_id
                .ok_or_else(|| Error::Validation(
                    "missing required field: procedure_id".to_string(),
                ))?,
            deployment_id: self.deployment_id.unwrap_or(NullableField::Absent),
            procedure_version: self.procedure_version.unwrap_or(NullableField::Absent),
            operated_by: self.operated_by,
            started_at: self.started_at
                .ok_or_else(|| Error::Validation(
                    "missing required field: started_at".to_string(),
                ))?,
            ended_at: self.ended_at
                .ok_or_else(|| Error::Validation(
                    "missing required field: ended_at".to_string(),
                ))?,
            serial_number: self.serial_number
                .ok_or_else(|| Error::Validation(
                    "missing required field: serial_number".to_string(),
                ))?,
            part_number: self.part_number,
            revision_number: self.revision_number,
            batch_number: self.batch_number,
            sub_units: self.sub_units,
            docstring: self.docstring,
            phases: self.phases,
            logs: self.logs,
        };
        request = request.json(&body);

        let response = self.client.execute(
            request,
            "run-create",
            base_url,
        ).await?;
        let result: RunCreateResponse = response.json().await?;
        Ok(result)
    }
}

// ---------------------------------------------------------------------------
// DeleteBuilder
// ---------------------------------------------------------------------------

/// Builder for [`RunsClient::delete`].
///
/// # Example
///
/// ```no_run
/// use tofupilot::TofuPilot;
///
/// # #[tokio::main]
/// # async fn main() -> tofupilot::Result<()> {
/// let client = TofuPilot::new("your-api-key");
/// let response = client.runs().delete()
///     .send()
///     .await?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct DeleteBuilder<'a> {
    client: &'a TofuPilot,
    ids: Option<Vec<String>>,
    server_url: Option<String>,
    timeout: Option<std::time::Duration>,
}

impl<'a> DeleteBuilder<'a> {
    pub(crate) fn new(client: &'a TofuPilot) -> Self {
        Self {
            client,
            ids: None,
            server_url: None,
            timeout: None,
        }
    }

    /// Set the `ids` query parameter.
    ///
    /// Run IDs to delete.
    pub fn ids(mut self, value: impl Into<Vec<String>>) -> Self {
        self.ids = Some(value.into());
        self
    }

    /// Override the server URL for this request.
    pub fn server_url(mut self, url: impl Into<String>) -> Self {
        self.server_url = Some(url.into());
        self
    }

    /// Override the timeout for this request.
    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    /// Send the request.
    pub async fn send(self) -> Result<RunDeleteResponse> {

        let base_url = self.server_url
            .as_deref()
            .unwrap_or(&self.client.config.base_url);

        let url = format!("{}{}", base_url, "/v2/runs");

        let mut request = self.client.http.request(
            reqwest::Method::DELETE,
            &url,
        );

        if let Some(timeout) = self.timeout {
            request = request.timeout(timeout);
        }

        if let Some(ref val) = self.ids {
            for item in val {
                request = request.query(&[("ids", item.to_string())]);
            }
        }


        let response = self.client.execute(
            request,
            "run-delete",
            base_url,
        ).await?;
        let result: RunDeleteResponse = response.json().await?;
        Ok(result)
    }
}

// ---------------------------------------------------------------------------
// GetBuilder
// ---------------------------------------------------------------------------

/// Builder for [`RunsClient::get`].
///
/// # Example
///
/// ```no_run
/// use tofupilot::TofuPilot;
///
/// # #[tokio::main]
/// # async fn main() -> tofupilot::Result<()> {
/// let client = TofuPilot::new("your-api-key");
/// let response = client.runs().get()
///     .id("value")
///     .send()
///     .await?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct GetBuilder<'a> {
    client: &'a TofuPilot,
    id: Option<String>,
    server_url: Option<String>,
    timeout: Option<std::time::Duration>,
}

impl<'a> GetBuilder<'a> {
    pub(crate) fn new(client: &'a TofuPilot) -> Self {
        Self {
            client,
            id: None,
            server_url: None,
            timeout: None,
        }
    }

    /// Set the `id` path parameter.
    ///
    /// ID of the run to retrieve.
    pub fn id(mut self, value: impl Into<String>) -> Self {
        self.id = Some(value.into());
        self
    }

    /// Override the server URL for this request.
    pub fn server_url(mut self, url: impl Into<String>) -> Self {
        self.server_url = Some(url.into());
        self
    }

    /// Override the timeout for this request.
    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    /// Send the request.
    pub async fn send(self) -> Result<RunGetResponse> {
        let id = self.id
            .ok_or_else(|| Error::Validation(
                "missing required path parameter: id".to_string(),
            ))?;
        let id_encoded = utf8_percent_encode(&id, URI_COMPONENT).to_string();

        let base_url = self.server_url
            .as_deref()
            .unwrap_or(&self.client.config.base_url);

        let url = format!(
            "{}/v2/runs/{id}",
            base_url,
            id = id_encoded,
        );

        let mut request = self.client.http.request(
            reqwest::Method::GET,
            &url,
        );

        if let Some(timeout) = self.timeout {
            request = request.timeout(timeout);
        }



        let response = self.client.execute(
            request,
            "run-get",
            base_url,
        ).await?;
        let result: RunGetResponse = response.json().await?;
        Ok(result)
    }
}

// ---------------------------------------------------------------------------
// UpdateBuilder
// ---------------------------------------------------------------------------

/// Builder for [`RunsClient::update`].
///
/// # Example
///
/// ```no_run
/// use tofupilot::TofuPilot;
///
/// # #[tokio::main]
/// # async fn main() -> tofupilot::Result<()> {
/// let client = TofuPilot::new("your-api-key");
/// let response = client.runs().update()
///     .send()
///     .await?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct UpdateBuilder<'a> {
    client: &'a TofuPilot,
    id: Option<String>,
    attachments: Option<Vec<String>>,
    server_url: Option<String>,
    timeout: Option<std::time::Duration>,
}

impl<'a> UpdateBuilder<'a> {
    pub(crate) fn new(client: &'a TofuPilot) -> Self {
        Self {
            client,
            id: None,
            attachments: None,
            server_url: None,
            timeout: None,
        }
    }

    /// Set the `id` path parameter.
    ///
    /// Unique identifier of the run to update.
    pub fn id(mut self, value: impl Into<String>) -> Self {
        self.id = Some(value.into());
        self
    }

    /// Set the `attachments` field.
    ///
    /// Array of upload IDs to attach to the run.
    pub fn attachments(mut self, value: impl Into<Vec<String>>) -> Self {
        self.attachments = Some(value.into());
        self
    }

    /// Set the full request body (alternative to setting individual fields).
    pub fn body(mut self, body: RunUpdateRequestBody) -> Self {
        if body.attachments.is_some() {
            self.attachments = body.attachments;
        }
        self
    }

    /// Override the server URL for this request.
    pub fn server_url(mut self, url: impl Into<String>) -> Self {
        self.server_url = Some(url.into());
        self
    }

    /// Override the timeout for this request.
    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    /// Send the request.
    pub async fn send(self) -> Result<RunUpdateResponse> {
        let id = self.id
            .ok_or_else(|| Error::Validation(
                "missing required path parameter: id".to_string(),
            ))?;
        let id_encoded = utf8_percent_encode(&id, URI_COMPONENT).to_string();

        let base_url = self.server_url
            .as_deref()
            .unwrap_or(&self.client.config.base_url);

        let url = format!(
            "{}/v2/runs/{id}",
            base_url,
            id = id_encoded,
        );

        let mut request = self.client.http.request(
            reqwest::Method::PATCH,
            &url,
        );

        if let Some(timeout) = self.timeout {
            request = request.timeout(timeout);
        }


        let body = RunUpdateRequestBody {
            attachments: self.attachments,
        };
        request = request.json(&body);

        let response = self.client.execute(
            request,
            "run-update",
            base_url,
        ).await?;
        let result: RunUpdateResponse = response.json().await?;
        Ok(result)
    }
}

// ---------------------------------------------------------------------------
// CreateAttachmentBuilder
// ---------------------------------------------------------------------------

/// Builder for [`RunsClient::create_attachment`].
///
/// # Example
///
/// ```no_run
/// use tofupilot::TofuPilot;
///
/// # #[tokio::main]
/// # async fn main() -> tofupilot::Result<()> {
/// let client = TofuPilot::new("your-api-key");
/// let response = client.runs().create_attachment()
///     .name("value")
///     .send()
///     .await?;
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct CreateAttachmentBuilder<'a> {
    client: &'a TofuPilot,
    id: Option<String>,
    name: Option<String>,
    server_url: Option<String>,
    timeout: Option<std::time::Duration>,
}

impl<'a> CreateAttachmentBuilder<'a> {
    pub(crate) fn new(client: &'a TofuPilot) -> Self {
        Self {
            client,
            id: None,
            name: None,
            server_url: None,
            timeout: None,
        }
    }

    /// Set the `id` path parameter.
    ///
    /// Unique identifier of the run to attach the file to.
    pub fn id(mut self, value: impl Into<String>) -> Self {
        self.id = Some(value.into());
        self
    }

    /// Set the `name` field.
    ///
    /// File name including extension (e.g. "report.pdf"). Used to determine content type and display name.
    pub fn name(mut self, value: impl Into<String>) -> Self {
        self.name = Some(value.into());
        self
    }

    /// Set the full request body (alternative to setting individual fields).
    pub fn body(mut self, body: RunCreateAttachmentRequestBody) -> Self {
        self.name = Some(body.name);
        self
    }

    /// Override the server URL for this request.
    pub fn server_url(mut self, url: impl Into<String>) -> Self {
        self.server_url = Some(url.into());
        self
    }

    /// Override the timeout for this request.
    pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
        self.timeout = Some(timeout);
        self
    }

    /// Send the request.
    pub async fn send(self) -> Result<RunCreateAttachmentResponse> {
        let id = self.id
            .ok_or_else(|| Error::Validation(
                "missing required path parameter: id".to_string(),
            ))?;
        let id_encoded = utf8_percent_encode(&id, URI_COMPONENT).to_string();

        let base_url = self.server_url
            .as_deref()
            .unwrap_or(&self.client.config.base_url);

        let url = format!(
            "{}/v2/runs/{id}/attachments",
            base_url,
            id = id_encoded,
        );

        let mut request = self.client.http.request(
            reqwest::Method::POST,
            &url,
        );

        if let Some(timeout) = self.timeout {
            request = request.timeout(timeout);
        }


        let body = RunCreateAttachmentRequestBody {
            name: self.name
                .ok_or_else(|| Error::Validation(
                    "missing required field: name".to_string(),
                ))?,
        };
        request = request.json(&body);

        let response = self.client.execute(
            request,
            "run-createAttachment",
            base_url,
        ).await?;
        let result: RunCreateAttachmentResponse = response.json().await?;
        Ok(result)
    }
}

