use std::path::PathBuf;

use serde::{Deserialize, Serialize};
use serde_json::Value;

pub mod fleet;
pub mod runtime;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Envelope<T> {
    pub request_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub thread_id: Option<String>,
    pub body: T,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ThreadStatus {
    Running,
    Idle,
    Completed,
    Failed,
    Paused,
    Archived,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SessionSource {
    Interactive,
    Resume,
    Fork,
    Api,
    Unknown,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Thread {
    pub id: String,
    pub preview: String,
    pub ephemeral: bool,
    pub model_provider: String,
    pub created_at: i64,
    pub updated_at: i64,
    pub status: ThreadStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub path: Option<PathBuf>,
    pub cwd: PathBuf,
    pub cli_version: String,
    pub source: SessionSource,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ThreadGoalStatus {
    Active,
    Paused,
    Blocked,
    UsageLimited,
    BudgetLimited,
    Complete,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ThreadGoal {
    pub thread_id: String,
    pub goal_id: String,
    pub objective: String,
    pub status: ThreadGoalStatus,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token_budget: Option<i64>,
    pub tokens_used: i64,
    pub time_used_seconds: i64,
    pub created_at: i64,
    pub updated_at: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadStartParams {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model_provider: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cwd: Option<PathBuf>,
    #[serde(default)]
    pub persist_extended_history: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadResumeParams {
    pub thread_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub history: Option<Vec<Value>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub path: Option<PathBuf>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model_provider: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cwd: Option<PathBuf>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub approval_policy: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sandbox: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub config: Option<Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub base_instructions: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub developer_instructions: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub personality: Option<String>,
    #[serde(default)]
    pub persist_extended_history: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadForkParams {
    pub thread_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub path: Option<PathBuf>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model_provider: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cwd: Option<PathBuf>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub approval_policy: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sandbox: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub config: Option<Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub base_instructions: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub developer_instructions: Option<String>,
    #[serde(default)]
    pub persist_extended_history: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadListParams {
    #[serde(default)]
    pub include_archived: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<usize>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadReadParams {
    pub thread_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadSetNameParams {
    pub thread_id: String,
    pub name: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadGoalSetParams {
    pub thread_id: String,
    pub objective: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub token_budget: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadGoalGetParams {
    pub thread_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadGoalClearParams {
    pub thread_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum ThreadRequest {
    Create {
        #[serde(default)]
        metadata: Value,
    },
    Start(ThreadStartParams),
    Resume(ThreadResumeParams),
    Fork(ThreadForkParams),
    List(ThreadListParams),
    Read(ThreadReadParams),
    SetName(ThreadSetNameParams),
    GoalSet(ThreadGoalSetParams),
    GoalGet(ThreadGoalGetParams),
    GoalClear(ThreadGoalClearParams),
    Archive {
        thread_id: String,
    },
    Unarchive {
        thread_id: String,
    },
    Message {
        thread_id: String,
        input: String,
    },
}

/// Response to a [`ThreadRequest`].
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThreadResponse {
    /// The thread this response pertains to.
    pub thread_id: String,
    /// Human-readable status string (e.g. `"ok"`, `"error"`).
    pub status: String,
    /// The thread details, when a single thread is returned.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub thread: Option<Thread>,
    /// List of threads, populated by `List` requests.
    #[serde(default)]
    pub threads: Vec<Thread>,
    /// Thread goal returned by goal get/set requests.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub goal: Option<ThreadGoal>,
    /// The model used for the thread, if applicable.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    /// The model provider used for the thread.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model_provider: Option<String>,
    /// The working directory of the thread.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cwd: Option<PathBuf>,
    /// The active approval policy.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub approval_policy: Option<String>,
    /// The active sandbox configuration.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sandbox: Option<String>,
    /// Streaming events associated with this response.
    #[serde(default)]
    pub events: Vec<EventFrame>,
    /// Arbitrary additional response data.
    #[serde(default)]
    pub data: Value,
}

/// Application-level requests that are not tied to a specific thread.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum AppRequest {
    /// Query the server's capabilities.
    Capabilities,
    /// Read a configuration value by key.
    ConfigGet { key: String },
    /// Set a configuration key to a value.
    ConfigSet { key: String, value: String },
    /// Remove a configuration key.
    ConfigUnset { key: String },
    /// List all configuration entries.
    ConfigList,
    /// List available models.
    Models,
    /// List threads that are currently loaded in memory.
    ThreadLoadedList,
}

/// Response to an [`AppRequest`].
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppResponse {
    /// Whether the request succeeded.
    pub ok: bool,
    /// The response payload.
    pub data: Value,
    /// Streaming events associated with this response.
    #[serde(default)]
    pub events: Vec<EventFrame>,
}

/// A simple prompt request that sends text to the model and returns output.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PromptRequest {
    /// Optional thread context for the prompt.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub thread_id: Option<String>,
    /// The prompt text.
    pub prompt: String,
    /// Model override, or the default if omitted.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
}

/// Response to a [`PromptRequest`].
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PromptResponse {
    /// The model's output text.
    pub output: String,
    /// The model that produced the output.
    pub model: String,
    /// Streaming events associated with this response.
    #[serde(default)]
    pub events: Vec<EventFrame>,
}

/// Policy controlling when the agent must ask the user for approval before acting.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AskForApproval {
    /// Ask for approval unless the action is on a trusted path/resource.
    UnlessTrusted,
    /// Only ask after a tool call fails.
    OnFailure,
    /// Ask every time a tool call is requested.
    OnRequest,
    /// Reject the action without asking, with details on which categories are blocked.
    Reject {
        sandbox_approval: bool,
        rules: bool,
        mcp_elicitations: bool,
    },
    /// Never ask; auto-approve all actions.
    Never,
}

/// Classification of tool invocation origin.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ToolKind {
    /// A built-in function tool.
    Function,
    /// An MCP (Model Context Protocol) tool.
    Mcp,
}

/// Parameters for executing a local shell command.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocalShellParams {
    /// The shell command to execute.
    pub command: String,
    /// Working directory for the command.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cwd: Option<String>,
    /// Timeout in milliseconds.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timeout_ms: Option<u64>,
}

/// The payload of a tool call, discriminated by tool type.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ToolPayload {
    /// A built-in function call with JSON-encoded arguments.
    Function { arguments: String },
    /// A custom tool invocation with a free-form input string.
    Custom { input: String },
    /// A local shell command execution.
    LocalShell { params: LocalShellParams },
    /// An MCP tool invocation targeting a specific server and tool.
    Mcp {
        server: String,
        tool: String,
        raw_arguments: Value,
        #[serde(skip_serializing_if = "Option::is_none")]
        raw_tool_call_id: Option<String>,
    },
}

/// The result of a tool call, discriminated by tool type.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ToolOutput {
    /// Result of a built-in function call.
    Function {
        /// The output body, if any.
        #[serde(skip_serializing_if = "Option::is_none")]
        body: Option<Value>,
        /// Whether the call succeeded.
        success: bool,
    },
    /// Result of an MCP tool call.
    Mcp {
        /// The result value returned by the MCP server.
        result: Value,
    },
}

/// Action to take for a network policy rule.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum NetworkPolicyRuleAction {
    /// Allow network access to the host.
    Allow,
    /// Deny network access to the host.
    Deny,
}

/// A proposed amendment to the network access policy for a specific host.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct NetworkPolicyAmendment {
    /// The host to amend the policy for.
    pub host: String,
    /// The action to apply.
    pub action: NetworkPolicyRuleAction,
}

/// A user's decision on an approval request.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ReviewDecision {
    /// Approve the action.
    Approved,
    /// Approve and also amend the execution policy.
    ApprovedExecpolicyAmendment,
    /// Approve for the remainder of this session only.
    ApprovedForSession,
    /// Approve with a network policy amendment.
    NetworkPolicyAmendment {
        host: String,
        action: NetworkPolicyRuleAction,
    },
    /// Deny the action.
    Denied,
    /// Abort the entire turn.
    Abort,
}

/// Status of an MCP server during startup.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum McpStartupStatus {
    /// The server is in the process of starting.
    Starting,
    /// The server is ready to accept requests.
    Ready,
    /// The server failed to start.
    Failed { error: String },
    /// Startup was cancelled.
    Cancelled,
}

/// A progress update for a single MCP server's startup.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpStartupUpdateEvent {
    /// Name of the MCP server.
    pub server_name: String,
    /// Current startup status.
    pub status: McpStartupStatus,
}

/// Details of an MCP server that failed to start.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpStartupFailure {
    /// Name of the MCP server that failed.
    pub server_name: String,
    /// Error description.
    pub error: String,
}

/// Summary event emitted once all MCP servers have finished starting.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpStartupCompleteEvent {
    /// Servers that started successfully.
    pub ready: Vec<String>,
    /// Servers that failed to start.
    pub failed: Vec<McpStartupFailure>,
    /// Servers whose startup was cancelled.
    pub cancelled: Vec<String>,
}

/// Context about a network access request that requires approval.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkApprovalContext {
    /// The host being accessed.
    pub host: String,
    /// The network protocol (e.g. `"https"`, `"tcp"`).
    pub protocol: String,
}

/// An event requesting user approval for a command execution or patch application.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecApprovalRequestEvent {
    /// Identifier of the tool call requesting approval.
    pub call_id: String,
    /// Unique identifier for this approval request.
    pub approval_id: String,
    /// The turn during which the request was made.
    pub turn_id: String,
    /// The command that would be executed.
    pub command: String,
    /// The working directory for the command.
    pub cwd: String,
    /// Human-readable reason why approval is needed.
    pub reason: String,
    /// Policy rule that matched this approval request, when available.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub matched_rule: Option<Box<str>>,
    /// Network context if the approval involves network access.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub network_approval_context: Option<NetworkApprovalContext>,
    /// Proposed execution policy rule amendments.
    #[serde(default)]
    pub proposed_execpolicy_amendment: Vec<String>,
    /// Proposed network policy amendments.
    #[serde(default)]
    pub proposed_network_policy_amendments: Vec<NetworkPolicyAmendment>,
    /// Additional permissions being requested.
    #[serde(default)]
    pub additional_permissions: Vec<String>,
    /// The set of decisions the user can choose from.
    #[serde(default)]
    pub available_decisions: Vec<ReviewDecision>,
}

/// The channel a response delta is being written to.
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ResponseChannel {
    /// The main visible text output.
    #[default]
    Text,
    /// Internal reasoning / chain-of-thought output.
    Reasoning,
}

impl ResponseChannel {
    /// Returns `true` if this is the `Text` channel.
    pub const fn is_text(&self) -> bool {
        matches!(self, ResponseChannel::Text)
    }
}

/// A user's approval decision sent in response to an approval request.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalDecisionRequest {
    /// The decision identifier (e.g. `"approved"`, `"denied"`).
    pub decision: String,
    /// Whether to remember this decision for future similar requests.
    #[serde(default)]
    pub remember: bool,
}

/// A single streaming event frame emitted during agent execution.
///
/// Events are tagged by the `event` field and cover the full lifecycle of a
/// turn: response streaming, tool calls, MCP lifecycle, command execution,
/// patch application, approvals, and errors.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "event", rename_all = "snake_case")]
pub enum EventFrame {
    /// A new model response has started.
    ResponseStart { response_id: String },
    /// A incremental text delta for an in-progress response.
    ResponseDelta {
        response_id: String,
        delta: String,
        #[serde(default, skip_serializing_if = "ResponseChannel::is_text")]
        channel: ResponseChannel,
    },
    /// The model response has finished.
    ResponseEnd { response_id: String },
    /// A tool call has begun.
    ToolCallStart {
        response_id: String,
        tool_name: String,
        arguments: Value,
    },
    /// A tool call has completed and produced a result.
    ToolCallResult {
        response_id: String,
        tool_name: String,
        output: Value,
    },
    /// Progress update for an MCP server starting up.
    McpStartupUpdate { update: McpStartupUpdateEvent },
    /// All MCP servers have finished starting.
    McpStartupComplete { summary: McpStartupCompleteEvent },
    /// An MCP tool call has begun.
    McpToolCallBegin {
        server_name: String,
        tool_name: String,
    },
    /// An MCP tool call has finished.
    McpToolCallEnd {
        server_name: String,
        tool_name: String,
        ok: bool,
    },
    /// User approval is needed for a command execution.
    ExecApprovalRequest { request: ExecApprovalRequestEvent },
    /// User approval is needed for applying a patch.
    ApplyPatchApprovalRequest { request: ExecApprovalRequestEvent },
    /// An MCP server is requesting user input (elicitation).
    ElicitationRequest {
        server_name: String,
        request_id: String,
        prompt: String,
    },
    /// A command has started executing.
    ExecCommandBegin { command: String, cwd: String },
    /// Incremental output from a running command.
    ExecCommandOutputDelta { command: String, delta: String },
    /// A command has finished executing.
    ExecCommandEnd { command: String, exit_code: i32 },
    /// A patch has started being applied to a file.
    PatchApplyBegin { path: String },
    /// A patch has finished being applied.
    PatchApplyEnd { path: String, ok: bool },
    /// A new turn has started within a thread.
    TurnStarted { turn_id: String },
    /// A turn has completed successfully.
    TurnComplete { turn_id: String },
    /// A turn was aborted before completion.
    TurnAborted { turn_id: String, reason: String },
    /// A thread goal was set or updated.
    ThreadGoalUpdated { goal: ThreadGoal },
    /// A thread goal was cleared.
    ThreadGoalCleared { thread_id: String },
    /// An error occurred during processing.
    Error {
        response_id: String,
        message: String,
    },
}
