//! Fluent builder for creating Event instances.

use super::types::Event;
use crate::model::event_type::EventType;
use chrono::{DateTime, Utc};

/// Builder for creating Event instances with an ergonomic API.
///
/// Provides helper methods that group related fields together and accept
/// non-Option values directly, reducing verbosity compared to chained `with_*` calls.
///
/// # Method variants
///
/// - **Regular setters** (e.g., `framework()`, `model()`) accept values directly and always
///   set the field.
/// - **Optional setters** (e.g., `framework_opt()`, `model_opt()`) accept `Option<T>` and
///   only set the field if `Some` is passed; `None` preserves any previously set value.
/// - **Grouped helpers** (e.g., `tokens()`, `cache_tokens()`, `tool()`) set multiple related
///   fields at once for common patterns.
///
/// # Example
///
/// ```
/// use mi6_core::{EventBuilder, EventType};
///
/// // Building an API request event
/// let event = EventBuilder::new("machine-1", EventType::ApiRequest, "session-123")
///     .framework("claude")
///     .tokens(1000, 500)
///     .cache_tokens(200, 0)
///     .cost(0.05)
///     .model("claude-3-opus")
///     .build();
///
/// // Building a hook event
/// let event = EventBuilder::new("machine-1", EventType::PreToolUse, "session-123")
///     .framework("claude")
///     .tool("toolu_123", "Bash")
///     .payload(r#"{"command": "ls"}"#)
///     .build();
///
/// // Using _opt methods to conditionally set fields
/// let maybe_model: Option<String> = Some("claude-3-opus".to_string());
/// let event = EventBuilder::new("machine-1", EventType::ApiRequest, "session-123")
///     .model_opt(maybe_model)  // Sets model to "claude-3-opus"
///     .model_opt(None)         // Does nothing, preserves "claude-3-opus"
///     .build();
/// ```
#[derive(Debug, Clone)]
pub struct EventBuilder {
    event: Event,
}

impl EventBuilder {
    /// Create a new EventBuilder with required fields.
    ///
    /// The timestamp is set to the current time; use `timestamp()` to override.
    pub fn new(
        machine_id: impl Into<String>,
        event_type: EventType,
        session_id: impl Into<String>,
    ) -> Self {
        Self {
            event: Event::new(machine_id.into(), event_type, session_id.into()),
        }
    }

    /// Build the final Event.
    pub fn build(self) -> Event {
        self.event
    }

    // --- Grouped helper methods ---

    /// Set input and output tokens together.
    pub fn tokens(mut self, input: i64, output: i64) -> Self {
        self.event.tokens_input = Some(input);
        self.event.tokens_output = Some(output);
        self
    }

    /// Set cache read and cache write tokens together.
    pub fn cache_tokens(mut self, read: i64, write: i64) -> Self {
        self.event.tokens_cache_read = Some(read);
        self.event.tokens_cache_write = Some(write);
        self
    }

    /// Set tool_use_id and tool_name together (common for tool events).
    pub fn tool(mut self, tool_use_id: impl Into<String>, tool_name: impl Into<String>) -> Self {
        self.event.tool_use_id = Some(tool_use_id.into());
        self.event.tool_name = Some(tool_name.into());
        self
    }

    // --- Individual field setters (non-Option convenience) ---

    /// Set the framework.
    pub fn framework(mut self, framework: impl Into<String>) -> Self {
        self.event.framework = Some(framework.into());
        self
    }

    /// Set the model.
    pub fn model(mut self, model: impl Into<String>) -> Self {
        self.event.model = Some(model.into());
        self
    }

    /// Set the cost in USD.
    pub fn cost(mut self, cost_usd: f64) -> Self {
        self.event.cost_usd = Some(cost_usd);
        self
    }

    /// Set the duration in milliseconds.
    pub fn duration_ms(mut self, duration: i64) -> Self {
        self.event.duration_ms = Some(duration);
        self
    }

    /// Set the timestamp.
    pub fn timestamp(mut self, timestamp: DateTime<Utc>) -> Self {
        self.event.timestamp = timestamp;
        self
    }

    /// Set the payload JSON string.
    pub fn payload(mut self, payload: impl Into<String>) -> Self {
        self.event.payload = Some(payload.into());
        self
    }

    /// Set the metadata JSON string.
    pub fn metadata(mut self, metadata: impl Into<String>) -> Self {
        self.event.metadata = Some(metadata.into());
        self
    }

    /// Set the tool_use_id.
    pub fn tool_use_id(mut self, tool_use_id: impl Into<String>) -> Self {
        self.event.tool_use_id = Some(tool_use_id.into());
        self
    }

    /// Set the tool_name.
    pub fn tool_name(mut self, tool_name: impl Into<String>) -> Self {
        self.event.tool_name = Some(tool_name.into());
        self
    }

    /// Set the spawned_agent_id.
    pub fn spawned_agent_id(mut self, spawned_agent_id: impl Into<String>) -> Self {
        self.event.spawned_agent_id = Some(spawned_agent_id.into());
        self
    }

    /// Set the subagent_type.
    pub fn subagent_type(mut self, subagent_type: impl Into<String>) -> Self {
        self.event.subagent_type = Some(subagent_type.into());
        self
    }

    /// Set the permission_mode.
    pub fn permission_mode(mut self, permission_mode: impl Into<String>) -> Self {
        self.event.permission_mode = Some(permission_mode.into());
        self
    }

    /// Set the transcript_path.
    pub fn transcript_path(mut self, transcript_path: impl Into<String>) -> Self {
        self.event.transcript_path = Some(transcript_path.into());
        self
    }

    /// Set the cwd.
    pub fn cwd(mut self, cwd: impl Into<String>) -> Self {
        self.event.cwd = Some(cwd.into());
        self
    }

    /// Set the git_branch.
    pub fn git_branch(mut self, git_branch: impl Into<String>) -> Self {
        self.event.git_branch = Some(git_branch.into());
        self
    }

    /// Set the pid.
    pub fn pid(mut self, pid: i32) -> Self {
        self.event.pid = Some(pid);
        self
    }

    /// Set the process_start_time.
    pub fn process_start_time(mut self, start_time: i64) -> Self {
        self.event.process_start_time = Some(start_time);
        self
    }

    // --- Optional field setters (for passing through Option values) ---

    /// Set the framework if Some.
    pub fn framework_opt(mut self, framework: Option<String>) -> Self {
        if let Some(f) = framework {
            self.event.framework = Some(f);
        }
        self
    }

    /// Set the timestamp if Some.
    pub fn timestamp_opt(mut self, timestamp: Option<DateTime<Utc>>) -> Self {
        if let Some(ts) = timestamp {
            self.event.timestamp = ts;
        }
        self
    }

    /// Set the tool_use_id if Some.
    pub fn tool_use_id_opt(mut self, tool_use_id: Option<String>) -> Self {
        if let Some(id) = tool_use_id {
            self.event.tool_use_id = Some(id);
        }
        self
    }

    /// Set the tool_name if Some.
    pub fn tool_name_opt(mut self, tool_name: Option<String>) -> Self {
        if let Some(name) = tool_name {
            self.event.tool_name = Some(name);
        }
        self
    }

    /// Set the spawned_agent_id if Some.
    pub fn spawned_agent_id_opt(mut self, spawned_agent_id: Option<String>) -> Self {
        if let Some(id) = spawned_agent_id {
            self.event.spawned_agent_id = Some(id);
        }
        self
    }

    /// Set the subagent_type if Some.
    pub fn subagent_type_opt(mut self, subagent_type: Option<String>) -> Self {
        if let Some(t) = subagent_type {
            self.event.subagent_type = Some(t);
        }
        self
    }

    /// Set the permission_mode if Some.
    pub fn permission_mode_opt(mut self, permission_mode: Option<String>) -> Self {
        if let Some(m) = permission_mode {
            self.event.permission_mode = Some(m);
        }
        self
    }

    /// Set the transcript_path if Some.
    pub fn transcript_path_opt(mut self, transcript_path: Option<String>) -> Self {
        if let Some(p) = transcript_path {
            self.event.transcript_path = Some(p);
        }
        self
    }

    /// Set the cwd if Some.
    pub fn cwd_opt(mut self, cwd: Option<String>) -> Self {
        if let Some(c) = cwd {
            self.event.cwd = Some(c);
        }
        self
    }

    /// Set the git_branch if Some.
    pub fn git_branch_opt(mut self, git_branch: Option<String>) -> Self {
        if let Some(b) = git_branch {
            self.event.git_branch = Some(b);
        }
        self
    }

    /// Set the pid if Some.
    pub fn pid_opt(mut self, pid: Option<i32>) -> Self {
        if let Some(p) = pid {
            self.event.pid = Some(p);
        }
        self
    }

    /// Set the process_start_time if Some.
    pub fn process_start_time_opt(mut self, start_time: Option<i64>) -> Self {
        if let Some(t) = start_time {
            self.event.process_start_time = Some(t);
        }
        self
    }

    /// Set the model if Some.
    pub fn model_opt(mut self, model: Option<String>) -> Self {
        if let Some(m) = model {
            self.event.model = Some(m);
        }
        self
    }

    /// Set the tokens_cache_read if Some.
    pub fn tokens_cache_read_opt(mut self, tokens: Option<i64>) -> Self {
        if let Some(t) = tokens {
            self.event.tokens_cache_read = Some(t);
        }
        self
    }

    /// Set the tokens_cache_write if Some.
    pub fn tokens_cache_write_opt(mut self, tokens: Option<i64>) -> Self {
        if let Some(t) = tokens {
            self.event.tokens_cache_write = Some(t);
        }
        self
    }

    /// Set the cost_usd if Some.
    pub fn cost_opt(mut self, cost: Option<f64>) -> Self {
        if let Some(c) = cost {
            self.event.cost_usd = Some(c);
        }
        self
    }

    /// Set the duration_ms if Some.
    pub fn duration_ms_opt(mut self, duration: Option<i64>) -> Self {
        if let Some(d) = duration {
            self.event.duration_ms = Some(d);
        }
        self
    }

    /// Set the metadata if Some.
    pub fn metadata_opt(mut self, metadata: Option<String>) -> Self {
        if let Some(m) = metadata {
            self.event.metadata = Some(m);
        }
        self
    }

    /// Set the payload if Some.
    pub fn payload_opt(mut self, payload: Option<String>) -> Self {
        if let Some(p) = payload {
            self.event.payload = Some(p);
        }
        self
    }

    /// Set the data source (hook, otel, transcript).
    pub fn source(mut self, source: impl Into<String>) -> Self {
        self.event.source = Some(source.into());
        self
    }

    /// Set whether this is a sidechain request.
    pub fn is_sidechain(mut self, is_sidechain: bool) -> Self {
        self.event.is_sidechain = Some(is_sidechain);
        self
    }

    /// Set is_sidechain if Some.
    pub fn is_sidechain_opt(mut self, is_sidechain: Option<bool>) -> Self {
        if let Some(s) = is_sidechain {
            self.event.is_sidechain = Some(s);
        }
        self
    }
}
