use std::path::PathBuf;

use chrono::DateTime;
use chrono::Utc;
use codex_protocol::ThreadId;
use codex_protocol::dynamic_tools::DynamicToolSpec;
use codex_protocol::models::BaseInstructions;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::GitInfo;
use codex_protocol::protocol::RolloutItem;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::ThreadMemoryMode as MemoryMode;
use codex_protocol::protocol::TokenUsage;
use serde::Deserialize;
use serde::Serialize;

/// Controls how many event variants should be persisted for future replay.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum ThreadEventPersistenceMode {
    /// Persist only the legacy minimal replay surface.
    #[default]
    Limited,
    /// Persist the richer event surface used by app-server history reconstruction.
    Extended,
}

/// Parameters required to create a persisted thread.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateThreadParams {
    /// Thread id generated by Codex before opening persistence.
    pub thread_id: ThreadId,
    /// Source thread id when this thread is created as a fork.
    pub forked_from_id: Option<ThreadId>,
    /// Runtime source for the thread.
    pub source: SessionSource,
    /// Base instructions persisted in session metadata.
    pub base_instructions: BaseInstructions,
    /// Dynamic tools available to the thread at startup.
    pub dynamic_tools: Vec<DynamicToolSpec>,
    /// Whether persistence should include the extended event surface.
    pub event_persistence_mode: ThreadEventPersistenceMode,
}

/// Parameters required to reopen persistence for an existing thread.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ResumeThreadParams {
    /// Existing thread id whose future items should be appended.
    pub thread_id: ThreadId,
    /// Known local rollout path when the caller resumed from a specific file.
    pub rollout_path: Option<PathBuf>,
    /// Known replay history for the resumed thread, if already loaded by the caller.
    pub history: Option<Vec<RolloutItem>>,
    /// Whether archived threads may be reopened.
    pub include_archived: bool,
    /// Whether persistence should include the extended event surface.
    pub event_persistence_mode: ThreadEventPersistenceMode,
}

/// Parameters for appending rollout items to a live thread.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AppendThreadItemsParams {
    /// Thread id to append to.
    pub thread_id: ThreadId,
    /// Items to append in order.
    pub items: Vec<RolloutItem>,
}

/// Parameters for loading persisted history for resume, fork, rollback, and memory jobs.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct LoadThreadHistoryParams {
    /// Thread id to load.
    pub thread_id: ThreadId,
    /// Whether archived threads are eligible.
    pub include_archived: bool,
}

/// Persisted rollout history for a thread, without any filesystem path requirement.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StoredThreadHistory {
    /// Thread id represented by the history.
    pub thread_id: ThreadId,
    /// Persisted rollout items in replay order.
    pub items: Vec<RolloutItem>,
}

/// Parameters for reading a thread summary and optionally its replay history.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReadThreadParams {
    /// Thread id to read.
    pub thread_id: ThreadId,
    /// Whether archived threads are eligible.
    pub include_archived: bool,
    /// Whether persisted rollout items should be included in the response.
    pub include_history: bool,
}

/// Parameters for reading a local rollout-backed thread by path.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReadThreadByRolloutPathParams {
    /// Local rollout JSONL path to read.
    pub rollout_path: PathBuf,
    /// Whether archived threads are eligible.
    pub include_archived: bool,
    /// Whether persisted rollout items should be included in the response.
    pub include_history: bool,
}

/// The sort key to use when listing stored threads.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum ThreadSortKey {
    /// Sort by the thread creation timestamp.
    #[default]
    CreatedAt,
    /// Sort by the thread last-update timestamp.
    UpdatedAt,
}

/// The direction to use when listing stored threads.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum SortDirection {
    /// Return older threads before newer threads.
    Asc,
    /// Return newer threads before older threads.
    #[default]
    Desc,
}

/// Parameters for listing threads.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListThreadsParams {
    /// Maximum number of threads to return.
    pub page_size: usize,
    /// Opaque cursor returned by a previous list call.
    pub cursor: Option<String>,
    /// Sort order requested by the caller.
    pub sort_key: ThreadSortKey,
    /// Sort direction requested by the caller.
    pub sort_direction: SortDirection,
    /// Allowed session sources. Empty means implementation default.
    pub allowed_sources: Vec<SessionSource>,
    /// Optional model provider filter. `None` means implementation default, while an empty vector
    /// means all providers.
    pub model_providers: Option<Vec<String>>,
    /// Optional cwd filters. `None` means all working directories, while an empty vector matches no
    /// threads.
    pub cwd_filters: Option<Vec<PathBuf>>,
    /// Whether archived threads should be listed instead of active threads.
    pub archived: bool,
    /// Optional substring/full-text search term for thread title/preview.
    pub search_term: Option<String>,
    /// Return directly from the state DB without scanning JSONL rollouts to repair metadata.
    pub use_state_db_only: bool,
}

/// A page of stored thread records.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ThreadPage {
    /// Threads returned for this page.
    pub items: Vec<StoredThread>,
    /// Opaque cursor to continue listing.
    pub next_cursor: Option<String>,
}

/// Store-owned thread metadata used by list/read/resume responses.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StoredThread {
    /// Thread id.
    pub thread_id: ThreadId,
    /// Local rollout path when the backing store is filesystem-based.
    pub rollout_path: Option<PathBuf>,
    /// Source thread id when this thread was forked from another thread.
    pub forked_from_id: Option<ThreadId>,
    /// Best available user-facing preview, usually the first user message.
    pub preview: String,
    /// Optional user-facing thread name/title.
    pub name: Option<String>,
    /// Model provider id associated with the thread.
    pub model_provider: String,
    /// Latest observed model, if known.
    pub model: Option<String>,
    /// Latest observed reasoning effort, if known.
    pub reasoning_effort: Option<ReasoningEffort>,
    /// Thread creation timestamp.
    pub created_at: DateTime<Utc>,
    /// Thread last-update timestamp.
    pub updated_at: DateTime<Utc>,
    /// Thread archive timestamp, if archived.
    pub archived_at: Option<DateTime<Utc>>,
    /// Working directory captured for the thread.
    pub cwd: PathBuf,
    /// CLI version captured for the thread.
    pub cli_version: String,
    /// Runtime source for the thread.
    pub source: SessionSource,
    /// Optional random nickname for thread-spawn sub-agents.
    pub agent_nickname: Option<String>,
    /// Optional role for thread-spawn sub-agents.
    pub agent_role: Option<String>,
    /// Optional canonical path for thread-spawn sub-agents.
    pub agent_path: Option<String>,
    /// Optional Git metadata captured for the thread.
    pub git_info: Option<GitInfo>,
    /// Approval mode captured for the thread.
    pub approval_mode: AskForApproval,
    /// Sandbox policy captured for the thread.
    pub sandbox_policy: SandboxPolicy,
    /// Last observed token usage.
    pub token_usage: Option<TokenUsage>,
    /// First user message observed for this thread, if any.
    pub first_user_message: Option<String>,
    /// Persisted history, populated only when requested.
    pub history: Option<StoredThreadHistory>,
}

/// Optional field patch where omission leaves a value unchanged and `Some(None)` clears it.
pub type OptionalStringPatch = Option<Option<String>>;

/// Patch for thread Git metadata.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct GitInfoPatch {
    /// Replacement commit SHA, clear request, or no-op.
    pub sha: OptionalStringPatch,
    /// Replacement branch name, clear request, or no-op.
    pub branch: OptionalStringPatch,
    /// Replacement origin URL, clear request, or no-op.
    pub origin_url: OptionalStringPatch,
}

/// Patch for mutable thread metadata.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadMetadataPatch {
    /// Replacement user-facing thread name.
    pub name: Option<String>,
    /// Replacement thread memory behavior.
    pub memory_mode: Option<MemoryMode>,
    /// Optional Git metadata patch.
    pub git_info: Option<GitInfoPatch>,
}

/// Parameters for patching mutable thread metadata.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpdateThreadMetadataParams {
    /// Thread id to update.
    pub thread_id: ThreadId,
    /// Patch to apply.
    pub patch: ThreadMetadataPatch,
    /// Whether archived threads are eligible.
    pub include_archived: bool,
}

/// Parameters for archiving or unarchiving a thread.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ArchiveThreadParams {
    /// Thread id to archive or unarchive.
    pub thread_id: ThreadId,
}
