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::ThreadSource;
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,
}

/// Thread-scoped metadata used when opening live persistence.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadPersistenceMetadata {
    /// Effective working directory for environment-backed threads.
    ///
    /// `None` means the thread has no filesystem/environment context.
    pub cwd: Option<PathBuf>,
    /// Model provider associated with the thread.
    pub model_provider: String,
    /// Memory mode associated with the live thread.
    pub memory_mode: MemoryMode,
}

/// 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,
    /// Optional analytics source classification for this thread.
    pub thread_source: Option<ThreadSource>,
    /// Base instructions persisted in session metadata.
    pub base_instructions: BaseInstructions,
    /// Dynamic tools available to the thread at startup.
    pub dynamic_tools: Vec<DynamicToolSpec>,
    /// Metadata captured for the newly created thread.
    pub metadata: ThreadPersistenceMetadata,
    /// 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,
    /// Metadata for future writes appended to the resumed live thread.
    pub metadata: ThreadPersistenceMetadata,
    /// 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>,
}

/// Requested amount of item detail for stored turns.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum StoredTurnItemsView {
    /// Return turn metadata only.
    NotLoaded,
    /// Return display summary items for each turn.
    #[default]
    Summary,
    /// Return every persisted item available for each turn.
    Full,
}

/// Store-owned status for a persisted turn.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum StoredTurnStatus {
    /// The turn completed normally.
    Completed,
    /// The turn was interrupted before normal completion.
    Interrupted,
    /// The turn failed.
    Failed,
    /// The turn is still in progress.
    InProgress,
}

/// Store-owned error details for a failed persisted turn.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StoredTurnError {
    /// User-visible error message.
    pub message: String,
    /// Optional additional detail for clients that expose expanded error context.
    pub additional_details: Option<String>,
}

/// Parameters for listing turns within a stored thread.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListTurnsParams {
    /// Thread id to read.
    pub thread_id: ThreadId,
    /// Whether archived threads are eligible.
    pub include_archived: bool,
    /// Opaque cursor returned by a previous list call.
    pub cursor: Option<String>,
    /// Maximum number of turns to return.
    pub page_size: usize,
    /// Sort direction requested by the caller.
    pub sort_direction: SortDirection,
    /// Requested amount of item detail for each returned turn.
    pub items_view: StoredTurnItemsView,
}

/// Store-owned turn representation used by turn pagination APIs.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StoredTurn {
    /// Turn id.
    pub turn_id: String,
    /// Persisted rollout items associated with this turn, according to `items_view`.
    pub items: Vec<RolloutItem>,
    /// Amount of item detail included in `items`.
    pub items_view: StoredTurnItemsView,
    /// Store-owned status for API layer projection.
    pub status: StoredTurnStatus,
    /// Error message when the turn failed.
    pub error: Option<StoredTurnError>,
    /// Unix timestamp (seconds) when the turn started.
    pub started_at: Option<i64>,
    /// Unix timestamp (seconds) when the turn completed.
    pub completed_at: Option<i64>,
    /// Duration between turn start and completion in milliseconds, if known.
    pub duration_ms: Option<i64>,
}

/// A page of stored turns.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TurnPage {
    /// Turns returned for this page.
    pub turns: Vec<StoredTurn>,
    /// Opaque cursor to continue listing.
    pub next_cursor: Option<String>,
    /// Opaque cursor for fetching in the opposite direction.
    pub backwards_cursor: Option<String>,
}

/// Parameters for listing persisted items within a single turn.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListItemsParams {
    /// Thread id to read.
    pub thread_id: ThreadId,
    /// Turn id to hydrate.
    pub turn_id: String,
    /// Whether archived threads are eligible.
    pub include_archived: bool,
    /// Opaque cursor returned by a previous list call.
    pub cursor: Option<String>,
    /// Maximum number of items to return.
    pub page_size: usize,
    /// Sort direction requested by the caller.
    pub sort_direction: SortDirection,
}

/// A page of persisted rollout items within a turn.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ItemPage {
    /// Items returned for this page.
    pub items: Vec<RolloutItem>,
    /// Opaque cursor to continue listing.
    pub next_cursor: Option<String>,
    /// Opaque cursor for fetching in the opposite direction.
    pub backwards_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 analytics source classification for this thread.
    pub thread_source: Option<ThreadSource>,
    /// 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,
}
