//! Actions returned by the application's update function.
//!
//! `update` is pure with respect to the runtime: it mutates
//! state in place and returns a list of `Action`s for the
//! runtime to enact (quit, dispatch a DSL command to the
//! database, etc.). Side effects belong here, not in update
//! itself, which keeps update directly testable without a Tokio
//! runtime, a real terminal, or a database.

use crate::app::EffectiveMode;
use crate::dsl::Command;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Action {
    /// Stop the event loop and exit cleanly.
    Quit,
    /// Hand a parsed DSL command to the database worker.
    ///
    /// `command` is the parsed AST that the worker executes;
    /// `source` is the original user-typed text, retained for
    /// `history.log` per ADR-0015 §5. The runtime feeds the
    /// result back as `AppEvent::DslSucceeded` /
    /// `AppEvent::DslFailed`.
    ExecuteDsl {
        command: Command,
        source: String,
        /// The effective mode the line was submitted under (ADR-0037):
        /// `Simple` / `AdvancedPersistent` / `AdvancedOneShot`. Output-only
        /// — execution semantics do not depend on it; the runtime uses it
        /// to gate the DSL → SQL teaching echo (ADR-0038), which fires for
        /// DSL-form commands submitted in an advanced effective mode.
        submission_mode: EffectiveMode,
    },
    /// Record a *failed* submission to `history.log` as an `err`
    /// record (ADR-0034 §1/§2). Emitted by the pure-sync `App`
    /// for both failure kinds — a line that failed to parse (at
    /// submit) and a command the worker rejected (on
    /// `AppEvent::DslFailed`) — because the App does no I/O. The
    /// runtime appends best-effort: a failure to record a failure
    /// must never escalate a user error into a fatal (ADR-0034
    /// §4). `source` is the original user-typed text.
    JournalFailure {
        source: String,
        /// Whether the failed submission was advanced (ADR-0052): tags the
        /// `err` record `err:adv` so a failed advanced command hydrates in
        /// its `:`-prefixed form, recallable in simple mode. App commands
        /// (mode-agnostic) are `false`.
        advanced: bool,
    },
    /// User issued the `rebuild` app-level command (ADR-0015
    /// §7, §11). Runtime computes a summary from
    /// `project.yaml` + `data/` and posts back as
    /// `AppEvent::RebuildPrepared`, which opens the
    /// confirmation modal.
    PrepareRebuild,
    /// User confirmed the rebuild from inside the modal.
    /// Runtime wipes the current schema/data and reconstructs
    /// from text sources.
    Rebuild {
        source: String,
    },
    /// Open the load-picker modal. Runtime lists projects in
    /// the active data root and posts back as
    /// `AppEvent::LoadPickerReady`.
    OpenLoadPicker,
    /// Switch to the project at `path` (absolute or relative
    /// to the active data root). Runtime drops the current
    /// project, opens the new one, refreshes app state.
    LoadProject {
        path: std::path::PathBuf,
        source: String,
    },
    /// Save the current project to `target` and switch to it.
    /// `target` is a name or absolute path; relative names
    /// resolve against `<data-root>/projects/` per ADR-0015 §1.
    SaveAs {
        target: String,
        source: String,
    },
    /// Close the current project (auto-save guarantees state
    /// is on disk) and create a fresh auto-named temp.
    NewProject {
        source: String,
    },
    /// Export the current project to a zip file. `target` is
    /// `None` for the default filename
    /// (`YYYYMMDD-<projectname>-export-NN.zip`) under the
    /// active data root, or `Some(path)` for an explicit
    /// target. Relative paths resolve under
    /// `<data-root>/`. Per ADR-0015 §11 the zip excludes
    /// `playground.db` and `history.log`.
    Export {
        target: Option<String>,
        source: String,
    },
    /// Import a previously-exported zip and switch to the
    /// resulting project. `zip_path` is the user-typed path to
    /// the source archive (relative to CWD or absolute).
    /// `as_target` is the optional user-supplied destination
    /// name; when `None`, the destination is derived from the
    /// zip's top-level folder. Collisions auto-suffix `-NN`
    /// (ADR-0015 §11 amendment).
    Import {
        zip_path: String,
        as_target: Option<String>,
        source: String,
    },
    /// Replay a script of DSL commands from a file. The runtime
    /// reads the file, iterates non-blank/non-comment lines, and
    /// dispatches each through the same path as interactive
    /// input. On per-line failure the runtime reports the line
    /// number and stops (no rollback). On success it reports the
    /// number of commands run.
    ///
    /// `path` is the literal user-typed path; the runtime
    /// resolves relative paths against the active project's root
    /// so `replay history.log` works inside any project. Replay
    /// itself is NOT written to `history.log` — only the
    /// individual commands it dispatches are, since they are
    /// what mutate state.
    Replay {
        path: String,
    },
    /// User issued `undo` (`PrepareUndo`) or `redo` (`PrepareRedo`)
    /// (ADR-0006 Amendment 1). The runtime peeks the snapshot the
    /// command would restore and posts `AppEvent::UndoPrepared`
    /// (opening the confirmation modal) or `AppEvent::UndoUnavailable`
    /// (nothing to undo/redo). Only emitted when undo is enabled —
    /// the `App` notes "undo is off" itself under `--no-undo`.
    PrepareUndo,
    PrepareRedo,
    /// User confirmed `undo` / `redo` from inside the modal. The
    /// runtime restores the snapshot through the worker, then
    /// refreshes the table list + schema cache.
    Undo,
    Redo,
    /// Copy text to the system clipboard (the `copy` command, ADR-0041).
    /// The `App` builds the full text from `App.output` (so `update`
    /// stays pure); the runtime performs the I/O — emit an OSC 52 escape
    /// to the terminal *and* a best-effort native write, ignoring native
    /// failure (a headless host is fine; OSC 52 carried it).
    CopyToClipboard(String),
    /// User changed the input mode mid-session (the `mode` command).
    /// The runtime records it through the worker so `project.yaml`
    /// reflects the live mode and it is restored on the next open
    /// (ADR-0015 mode-restore amendment, issue #14). Best-effort:
    /// a persistence failure here must not escalate a UI mode toggle
    /// into a fatal — the in-memory mode has already changed.
    PersistMode(crate::mode::Mode),
}
