//! Browser-generic commands sent from the host application to a backend.
//!
//! This module defines request/operation inputs (`BrowserCommand`) used to
//! control browser lifecycle, navigation, and input handling.

use crate::data::{
    auxiliary_window::{AuxiliaryWindowId, AuxiliaryWindowResponse},
    background::BackgroundPolicy,
    browsing_context_open::BrowsingContextOpenResponse,
    dialog::DialogResponse,
    download::DownloadId,
    drag::{DragDrop, DragUpdate, ExternalDragDrop, ExternalDragEnter, ExternalDragUpdate},
    edit::EditAction,
    execution::BrowsingContextExecutionState,
    ids::{BrowsingContextId, TransientBrowsingContextId},
    ime::{ConfirmCompositionBehavior, ImeCommitText, ImeComposition},
    ipc::{BrowsingContextIpcMessage, IpcConfig},
    key::KeyEvent,
    mouse::{MouseEvent, MouseWheelEvent},
    navigation::NavigationEntryId,
    policy::BrowsingContextPolicy,
    transient_browsing_context::{TransientImeCommitText, TransientImeComposition},
    visibility::BrowsingContextVisibility,
    window_open::WindowOpenResponse,
};

/// Commands issued by the UI process (or app) to the browser backend.
///
/// This is intentionally expressed in "browser generic" vocabulary so that
/// `cbf` can be reused outside of Atelier.
#[derive(Debug, Clone)]
pub enum BrowserCommand {
    /// Request the backend to shutdown with a request id.
    ///
    /// Backends may stop their event stream shortly after receiving this.
    Shutdown { request_id: u64 },
    /// Confirm whether shutdown should proceed.
    ConfirmShutdown { request_id: u64, proceed: bool },
    /// Force shutdown without unload confirmations.
    ForceShutdown,

    /// Confirm a beforeunload dialog request.
    ConfirmBeforeUnload {
        browsing_context_id: BrowsingContextId,
        request_id: u64,
        proceed: bool,
    },
    /// Respond to a JavaScript dialog request for a page.
    RespondJavaScriptDialog {
        browsing_context_id: BrowsingContextId,
        request_id: u64,
        response: DialogResponse,
    },
    /// Respond to a JavaScript dialog request for a transient browsing context.
    RespondJavaScriptDialogInTransientBrowsingContext {
        transient_browsing_context_id: TransientBrowsingContextId,
        request_id: u64,
        response: DialogResponse,
    },
    /// Confirm a permission request.
    ConfirmPermission {
        browsing_context_id: BrowsingContextId,
        request_id: u64,
        allow: bool,
    },

    /// Create a new browsing context.
    ///
    /// - `initial_url`: If `None`, the backend may create an empty page.
    /// - `profile_id`: A canonical backend-issued profile identifier from `ListProfiles`.
    CreateBrowsingContext {
        request_id: u64,
        initial_url: Option<String>,
        profile_id: String,
        policy: Option<BrowsingContextPolicy>,
    },

    /// Fetch the list of available profiles from the backend.
    ListProfiles,
    /// Fetch the list of available extensions from the backend.
    ListExtensions { profile_id: String },

    /// Request to close a web page.
    RequestCloseBrowsingContext {
        browsing_context_id: BrowsingContextId,
    },
    /// Start a non-destructive close transaction for multiple browsing contexts.
    BeginCloseBrowsingContextsTransaction {
        request_id: u64,
        browsing_context_ids: Vec<BrowsingContextId>,
    },
    /// Commit a previously prepared browsing-context close transaction.
    CommitCloseBrowsingContextsTransaction { request_id: u64 },
    /// Cancel a previously prepared browsing-context close transaction.
    CancelCloseBrowsingContextsTransaction { request_id: u64 },
    /// Request to close a transient browsing context.
    CloseTransientBrowsingContext {
        transient_browsing_context_id: TransientBrowsingContextId,
    },

    /// Resize a web page surface.
    ResizeBrowsingContext {
        browsing_context_id: BrowsingContextId,
        width: u32,
        height: u32,
    },
    /// Resize a transient browsing context surface.
    ResizeTransientBrowsingContext {
        transient_browsing_context_id: TransientBrowsingContextId,
        width: u32,
        height: u32,
    },
    /// Update the page background policy of a web page surface.
    SetBrowsingContextBackgroundPolicy {
        browsing_context_id: BrowsingContextId,
        policy: BackgroundPolicy,
    },
    /// Update the page background policy of a transient browsing context surface.
    SetTransientBrowsingContextBackgroundPolicy {
        transient_browsing_context_id: TransientBrowsingContextId,
        policy: BackgroundPolicy,
    },

    // --- Navigation ---
    /// Navigate a page to the provided URL.
    Navigate {
        browsing_context_id: BrowsingContextId,
        url: String,
    },
    /// Navigate back in history for the page.
    GoBack {
        browsing_context_id: BrowsingContextId,
    },
    /// Navigate forward in history for the page.
    GoForward {
        browsing_context_id: BrowsingContextId,
    },
    /// Retrieve the current navigation history snapshot for the page.
    GetNavigationHistory {
        browsing_context_id: BrowsingContextId,
        request_id: u64,
    },
    /// Traverse the page history directly to the specified entry.
    TraverseHistoryToEntry {
        browsing_context_id: BrowsingContextId,
        entry_id: NavigationEntryId,
    },
    /// Traverse the page history by a relative offset.
    TraverseHistoryByOffset {
        browsing_context_id: BrowsingContextId,
        delta: i32,
    },
    /// Reload the current page, optionally bypassing caches.
    Reload {
        browsing_context_id: BrowsingContextId,
        ignore_cache: bool,
    },
    /// Open print preview for the current page content.
    PrintPreview {
        browsing_context_id: BrowsingContextId,
    },

    /// Request the DOM HTML of a web page.
    GetBrowsingContextDomHtml {
        browsing_context_id: BrowsingContextId,
        request_id: u64,
    },

    // --- Focus / Lifecycle ---
    /// Update whether the given web page should be focused for text input.
    SetBrowsingContextFocus {
        browsing_context_id: BrowsingContextId,
        focused: bool,
    },
    /// Update whether the transient browsing context should be focused for text input.
    SetTransientBrowsingContextFocus {
        transient_browsing_context_id: TransientBrowsingContextId,
        focused: bool,
    },
    /// Update whether the given web page should be treated as visible.
    SetBrowsingContextVisibility {
        browsing_context_id: BrowsingContextId,
        visibility: BrowsingContextVisibility,
    },
    /// Update whether the given web page should keep executing page activity.
    SetBrowsingContextExecutionState {
        browsing_context_id: BrowsingContextId,
        state: BrowsingContextExecutionState,
    },
    /// Enable browsing context IPC with explicit config.
    EnableIpc {
        browsing_context_id: BrowsingContextId,
        config: IpcConfig,
    },
    /// Disable browsing context IPC.
    DisableIpc {
        browsing_context_id: BrowsingContextId,
    },
    /// Post a host -> page IPC message.
    ///
    /// `message.channel` must be a non-empty string.
    PostBrowsingContextIpcMessage {
        browsing_context_id: BrowsingContextId,
        message: BrowsingContextIpcMessage,
    },

    // --- Input ---
    /// Deliver a keyboard event to the page.
    SendKeyEvent {
        browsing_context_id: BrowsingContextId,
        event: KeyEvent,
        commands: Vec<String>,
    },
    /// Execute a browser-generic edit action for the page.
    ExecuteEditAction {
        browsing_context_id: BrowsingContextId,
        action: EditAction,
    },
    /// Deliver a keyboard event to a transient browsing context.
    SendKeyEventToTransientBrowsingContext {
        transient_browsing_context_id: TransientBrowsingContextId,
        event: KeyEvent,
        commands: Vec<String>,
    },
    /// Execute a browser-generic edit action for a transient browsing context.
    ExecuteEditActionInTransientBrowsingContext {
        transient_browsing_context_id: TransientBrowsingContextId,
        action: EditAction,
    },
    /// Deliver a mouse event to the page.
    SendMouseEvent {
        browsing_context_id: BrowsingContextId,
        event: MouseEvent,
    },
    /// Deliver a mouse event to a transient browsing context.
    SendMouseEventToTransientBrowsingContext {
        transient_browsing_context_id: TransientBrowsingContextId,
        event: MouseEvent,
    },
    /// Deliver a mouse wheel event to the page.
    SendMouseWheelEvent {
        browsing_context_id: BrowsingContextId,
        event: MouseWheelEvent,
    },
    /// Deliver a mouse wheel event to a transient browsing context.
    SendMouseWheelEventToTransientBrowsingContext {
        transient_browsing_context_id: TransientBrowsingContextId,
        event: MouseWheelEvent,
    },
    /// Deliver drag update for host-owned drag session.
    ///
    /// Uses browser-generic drag payload. Backend-native details should stay
    /// in backend-specific extension layers.
    SendDragUpdate { update: DragUpdate },
    /// Deliver drag drop for host-owned drag session.
    SendDragDrop { drop: DragDrop },
    /// Cancel host-owned drag session.
    SendDragCancel {
        session_id: u64,
        browsing_context_id: BrowsingContextId,
    },
    /// Deliver a native external drag enter event to the page.
    SendExternalDragEnter { event: ExternalDragEnter },
    /// Deliver a native external drag update event to the page.
    SendExternalDragUpdate { event: ExternalDragUpdate },
    /// Notify the backend that the native external drag left the page.
    SendExternalDragLeave {
        browsing_context_id: BrowsingContextId,
    },
    /// Deliver a native external drag drop event to the page.
    SendExternalDragDrop { event: ExternalDragDrop },
    /// Update the current IME composition state.
    ///
    /// `ImeComposition` carries browser-generic span data and may include
    /// optional Chromium-specific style details per span.
    SetComposition { composition: ImeComposition },
    /// Commit IME text input to the focused element.
    ///
    /// `ImeCommitText` follows the same span boundary as `SetComposition`.
    CommitText { commit: ImeCommitText },
    /// Update the current IME composition state for a transient browsing context.
    SetTransientComposition {
        composition: TransientImeComposition,
    },
    /// Commit IME text input to the focused element in a transient browsing context.
    CommitTransientText { commit: TransientImeCommitText },
    /// Finish composing IME text with the given selection behavior.
    FinishComposingText {
        browsing_context_id: BrowsingContextId,
        behavior: ConfirmCompositionBehavior,
    },
    /// Finish composing IME text inside a transient browsing context.
    FinishComposingTextInTransientBrowsingContext {
        transient_browsing_context_id: TransientBrowsingContextId,
        behavior: ConfirmCompositionBehavior,
    },

    /// Accept a host-owned choice menu selection by request id.
    AcceptChoiceMenuSelection { request_id: u64, indices: Vec<i32> },

    /// Dismiss an open host-owned choice menu by request id.
    DismissChoiceMenu { request_id: u64 },

    /// Execute a context menu command by menu id.
    ExecuteContextMenuCommand {
        menu_id: u64,
        command_id: i32,
        event_flags: i32,
    },

    /// Dismiss an open context menu.
    DismissContextMenu { menu_id: u64 },

    /// Pause an in-progress download.
    PauseDownload { download_id: DownloadId },

    /// Resume a paused or resumable download.
    ResumeDownload { download_id: DownloadId },

    /// Cancel an active download.
    CancelDownload { download_id: DownloadId },

    /// Ask backend to open Chromium's default UI for a pending auxiliary request.
    OpenDefaultAuxiliaryWindow { profile_id: String, request_id: u64 },

    /// Respond to a pending auxiliary request with host-provided decision.
    RespondAuxiliaryWindow {
        profile_id: String,
        request_id: u64,
        response: AuxiliaryWindowResponse,
    },

    /// Request backend to close an auxiliary window/dialog.
    CloseAuxiliaryWindow {
        profile_id: String,
        window_id: AuxiliaryWindowId,
    },

    /// Respond to host-mediated browsing context open request.
    RespondBrowsingContextOpen {
        request_id: u64,
        response: BrowsingContextOpenResponse,
    },

    /// Respond to host-mediated window open request.
    RespondWindowOpen {
        request_id: u64,
        response: WindowOpenResponse,
    },
}

/// Browser operation associated with an execution path.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BrowserOperation {
    Shutdown,
    ConfirmShutdown,
    ForceShutdown,
    ConfirmBeforeUnload,
    RespondJavaScriptDialog,
    RespondJavaScriptDialogInTransientBrowsingContext,
    ConfirmPermission,
    CreateBrowsingContext,
    ListProfiles,
    ListExtensions,
    RequestCloseBrowsingContext,
    BeginCloseBrowsingContextsTransaction,
    CommitCloseBrowsingContextsTransaction,
    CancelCloseBrowsingContextsTransaction,
    CloseTransientBrowsingContext,
    ResizeBrowsingContext,
    ResizeTransientBrowsingContext,
    SetBrowsingContextBackgroundPolicy,
    SetTransientBrowsingContextBackgroundPolicy,
    Navigate,
    GoBack,
    GoForward,
    GetNavigationHistory,
    TraverseHistoryToEntry,
    TraverseHistoryByOffset,
    Reload,
    PrintPreview,
    GetBrowsingContextDomHtml,
    SetBrowsingContextFocus,
    SetTransientBrowsingContextFocus,
    SetBrowsingContextVisibility,
    SetBrowsingContextExecutionState,
    EnableIpc,
    DisableIpc,
    PostBrowsingContextIpcMessage,
    SendKeyEvent,
    ExecuteEditAction,
    SendKeyEventToTransientBrowsingContext,
    ExecuteEditActionInTransientBrowsingContext,
    SendMouseEvent,
    SendMouseEventToTransientBrowsingContext,
    SendMouseWheelEvent,
    SendMouseWheelEventToTransientBrowsingContext,
    SendDragUpdate,
    SendDragDrop,
    SendDragCancel,
    SendExternalDragEnter,
    SendExternalDragUpdate,
    SendExternalDragLeave,
    SendExternalDragDrop,
    SetComposition,
    CommitText,
    SetTransientComposition,
    CommitTransientText,
    FinishComposingText,
    FinishComposingTextInTransientBrowsingContext,
    AcceptChoiceMenuSelection,
    DismissChoiceMenu,
    ExecuteContextMenuCommand,
    DismissContextMenu,
    PauseDownload,
    ResumeDownload,
    CancelDownload,
    OpenDefaultAuxiliaryWindow,
    RespondAuxiliaryWindow,
    CloseAuxiliaryWindow,
    RespondBrowsingContextOpen,
    RespondWindowOpen,
}

impl BrowserOperation {
    pub fn from_command(command: &BrowserCommand) -> Self {
        match command {
            BrowserCommand::Shutdown { .. } => Self::Shutdown,
            BrowserCommand::ConfirmShutdown { .. } => Self::ConfirmShutdown,
            BrowserCommand::ForceShutdown => Self::ForceShutdown,
            BrowserCommand::ConfirmBeforeUnload { .. } => Self::ConfirmBeforeUnload,
            BrowserCommand::RespondJavaScriptDialog { .. } => Self::RespondJavaScriptDialog,
            BrowserCommand::RespondJavaScriptDialogInTransientBrowsingContext { .. } => {
                Self::RespondJavaScriptDialogInTransientBrowsingContext
            }
            BrowserCommand::ConfirmPermission { .. } => Self::ConfirmPermission,
            BrowserCommand::CreateBrowsingContext { .. } => Self::CreateBrowsingContext,
            BrowserCommand::ListProfiles => Self::ListProfiles,
            BrowserCommand::ListExtensions { .. } => Self::ListExtensions,
            BrowserCommand::RequestCloseBrowsingContext { .. } => Self::RequestCloseBrowsingContext,
            BrowserCommand::BeginCloseBrowsingContextsTransaction { .. } => {
                Self::BeginCloseBrowsingContextsTransaction
            }
            BrowserCommand::CommitCloseBrowsingContextsTransaction { .. } => {
                Self::CommitCloseBrowsingContextsTransaction
            }
            BrowserCommand::CancelCloseBrowsingContextsTransaction { .. } => {
                Self::CancelCloseBrowsingContextsTransaction
            }
            BrowserCommand::CloseTransientBrowsingContext { .. } => {
                Self::CloseTransientBrowsingContext
            }
            BrowserCommand::ResizeBrowsingContext { .. } => Self::ResizeBrowsingContext,
            BrowserCommand::ResizeTransientBrowsingContext { .. } => {
                Self::ResizeTransientBrowsingContext
            }
            BrowserCommand::SetBrowsingContextBackgroundPolicy { .. } => {
                Self::SetBrowsingContextBackgroundPolicy
            }
            BrowserCommand::SetTransientBrowsingContextBackgroundPolicy { .. } => {
                Self::SetTransientBrowsingContextBackgroundPolicy
            }
            BrowserCommand::Navigate { .. } => Self::Navigate,
            BrowserCommand::GoBack { .. } => Self::GoBack,
            BrowserCommand::GoForward { .. } => Self::GoForward,
            BrowserCommand::GetNavigationHistory { .. } => Self::GetNavigationHistory,
            BrowserCommand::TraverseHistoryToEntry { .. } => Self::TraverseHistoryToEntry,
            BrowserCommand::TraverseHistoryByOffset { .. } => Self::TraverseHistoryByOffset,
            BrowserCommand::Reload { .. } => Self::Reload,
            BrowserCommand::PrintPreview { .. } => Self::PrintPreview,
            BrowserCommand::GetBrowsingContextDomHtml { .. } => Self::GetBrowsingContextDomHtml,
            BrowserCommand::SetBrowsingContextFocus { .. } => Self::SetBrowsingContextFocus,
            BrowserCommand::SetTransientBrowsingContextFocus { .. } => {
                Self::SetTransientBrowsingContextFocus
            }
            BrowserCommand::SetBrowsingContextVisibility { .. } => {
                Self::SetBrowsingContextVisibility
            }
            BrowserCommand::SetBrowsingContextExecutionState { .. } => {
                Self::SetBrowsingContextExecutionState
            }
            BrowserCommand::EnableIpc { .. } => Self::EnableIpc,
            BrowserCommand::DisableIpc { .. } => Self::DisableIpc,
            BrowserCommand::PostBrowsingContextIpcMessage { .. } => {
                Self::PostBrowsingContextIpcMessage
            }
            BrowserCommand::SendKeyEvent { .. } => Self::SendKeyEvent,
            BrowserCommand::ExecuteEditAction { .. } => Self::ExecuteEditAction,
            BrowserCommand::SendKeyEventToTransientBrowsingContext { .. } => {
                Self::SendKeyEventToTransientBrowsingContext
            }
            BrowserCommand::ExecuteEditActionInTransientBrowsingContext { .. } => {
                Self::ExecuteEditActionInTransientBrowsingContext
            }
            BrowserCommand::SendMouseEvent { .. } => Self::SendMouseEvent,
            BrowserCommand::SendMouseEventToTransientBrowsingContext { .. } => {
                Self::SendMouseEventToTransientBrowsingContext
            }
            BrowserCommand::SendMouseWheelEvent { .. } => Self::SendMouseWheelEvent,
            BrowserCommand::SendMouseWheelEventToTransientBrowsingContext { .. } => {
                Self::SendMouseWheelEventToTransientBrowsingContext
            }
            BrowserCommand::SendDragUpdate { .. } => Self::SendDragUpdate,
            BrowserCommand::SendDragDrop { .. } => Self::SendDragDrop,
            BrowserCommand::SendDragCancel { .. } => Self::SendDragCancel,
            BrowserCommand::SendExternalDragEnter { .. } => Self::SendExternalDragEnter,
            BrowserCommand::SendExternalDragUpdate { .. } => Self::SendExternalDragUpdate,
            BrowserCommand::SendExternalDragLeave { .. } => Self::SendExternalDragLeave,
            BrowserCommand::SendExternalDragDrop { .. } => Self::SendExternalDragDrop,
            BrowserCommand::SetComposition { .. } => Self::SetComposition,
            BrowserCommand::CommitText { .. } => Self::CommitText,
            BrowserCommand::SetTransientComposition { .. } => Self::SetTransientComposition,
            BrowserCommand::CommitTransientText { .. } => Self::CommitTransientText,
            BrowserCommand::FinishComposingText { .. } => Self::FinishComposingText,
            BrowserCommand::FinishComposingTextInTransientBrowsingContext { .. } => {
                Self::FinishComposingTextInTransientBrowsingContext
            }
            BrowserCommand::AcceptChoiceMenuSelection { .. } => Self::AcceptChoiceMenuSelection,
            BrowserCommand::DismissChoiceMenu { .. } => Self::DismissChoiceMenu,
            BrowserCommand::ExecuteContextMenuCommand { .. } => Self::ExecuteContextMenuCommand,
            BrowserCommand::DismissContextMenu { .. } => Self::DismissContextMenu,
            BrowserCommand::PauseDownload { .. } => Self::PauseDownload,
            BrowserCommand::ResumeDownload { .. } => Self::ResumeDownload,
            BrowserCommand::CancelDownload { .. } => Self::CancelDownload,
            BrowserCommand::OpenDefaultAuxiliaryWindow { .. } => Self::OpenDefaultAuxiliaryWindow,
            BrowserCommand::RespondAuxiliaryWindow { .. } => Self::RespondAuxiliaryWindow,
            BrowserCommand::CloseAuxiliaryWindow { .. } => Self::CloseAuxiliaryWindow,
            BrowserCommand::RespondBrowsingContextOpen { .. } => Self::RespondBrowsingContextOpen,
            BrowserCommand::RespondWindowOpen { .. } => Self::RespondWindowOpen,
        }
    }
}

impl std::fmt::Display for BrowserOperation {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let operation = match self {
            Self::Shutdown => "shutdown",
            Self::ConfirmShutdown => "confirm_shutdown",
            Self::ForceShutdown => "force_shutdown",
            Self::ConfirmBeforeUnload => "confirm_beforeunload",
            Self::RespondJavaScriptDialog => "respond_javascript_dialog",
            Self::RespondJavaScriptDialogInTransientBrowsingContext => {
                "respond_javascript_dialog_in_transient_browsing_context"
            }
            Self::ConfirmPermission => "confirm_permission",
            Self::CreateBrowsingContext => "create_browsing_context",
            Self::ListProfiles => "list_profiles",
            Self::ListExtensions => "list_extensions",
            Self::RequestCloseBrowsingContext => "request_close_browsing_context",
            Self::BeginCloseBrowsingContextsTransaction => {
                "begin_close_browsing_contexts_transaction"
            }
            Self::CommitCloseBrowsingContextsTransaction => {
                "commit_close_browsing_contexts_transaction"
            }
            Self::CancelCloseBrowsingContextsTransaction => {
                "cancel_close_browsing_contexts_transaction"
            }
            Self::CloseTransientBrowsingContext => "close_transient_browsing_context",
            Self::ResizeBrowsingContext => "resize_browsing_context",
            Self::ResizeTransientBrowsingContext => "resize_transient_browsing_context",
            Self::SetBrowsingContextBackgroundPolicy => "set_browsing_context_background_policy",
            Self::SetTransientBrowsingContextBackgroundPolicy => {
                "set_transient_browsing_context_background_policy"
            }
            Self::Navigate => "navigate",
            Self::GoBack => "go_back",
            Self::GoForward => "go_forward",
            Self::GetNavigationHistory => "get_navigation_history",
            Self::TraverseHistoryToEntry => "traverse_history_to_entry",
            Self::TraverseHistoryByOffset => "traverse_history_by_offset",
            Self::Reload => "reload",
            Self::PrintPreview => "print_preview",
            Self::GetBrowsingContextDomHtml => "get_browsing_context_dom_html",
            Self::SetBrowsingContextFocus => "set_browsing_context_focus",
            Self::SetTransientBrowsingContextFocus => "set_transient_browsing_context_focus",
            Self::SetBrowsingContextVisibility => "set_browsing_context_visibility",
            Self::SetBrowsingContextExecutionState => "set_browsing_context_execution_state",
            Self::EnableIpc => "enable_ipc",
            Self::DisableIpc => "disable_ipc",
            Self::PostBrowsingContextIpcMessage => "post_browsing_context_ipc_message",
            Self::SendKeyEvent => "send_key_event",
            Self::ExecuteEditAction => "execute_edit_action",
            Self::SendKeyEventToTransientBrowsingContext => {
                "send_key_event_to_transient_browsing_context"
            }
            Self::ExecuteEditActionInTransientBrowsingContext => {
                "execute_edit_action_in_transient_browsing_context"
            }
            Self::SendMouseEvent => "send_mouse_event",
            Self::SendMouseEventToTransientBrowsingContext => {
                "send_mouse_event_to_transient_browsing_context"
            }
            Self::SendMouseWheelEvent => "send_mouse_wheel_event",
            Self::SendMouseWheelEventToTransientBrowsingContext => {
                "send_mouse_wheel_event_to_transient_browsing_context"
            }
            Self::SendDragUpdate => "send_drag_update",
            Self::SendDragDrop => "send_drag_drop",
            Self::SendDragCancel => "send_drag_cancel",
            Self::SendExternalDragEnter => "send_external_drag_enter",
            Self::SendExternalDragUpdate => "send_external_drag_update",
            Self::SendExternalDragLeave => "send_external_drag_leave",
            Self::SendExternalDragDrop => "send_external_drag_drop",
            Self::SetComposition => "set_composition",
            Self::CommitText => "commit_text",
            Self::SetTransientComposition => "set_transient_composition",
            Self::CommitTransientText => "commit_transient_text",
            Self::FinishComposingText => "finish_composing_text",
            Self::FinishComposingTextInTransientBrowsingContext => {
                "finish_composing_text_in_transient_browsing_context"
            }
            Self::AcceptChoiceMenuSelection => "accept_choice_menu_selection",
            Self::DismissChoiceMenu => "dismiss_choice_menu",
            Self::ExecuteContextMenuCommand => "execute_context_menu_command",
            Self::DismissContextMenu => "dismiss_context_menu",
            Self::PauseDownload => "pause_download",
            Self::ResumeDownload => "resume_download",
            Self::CancelDownload => "cancel_download",
            Self::OpenDefaultAuxiliaryWindow => "open_default_auxiliary_window",
            Self::RespondAuxiliaryWindow => "respond_auxiliary_window",
            Self::CloseAuxiliaryWindow => "close_auxiliary_window",
            Self::RespondBrowsingContextOpen => "respond_browsing_context_open",
            Self::RespondWindowOpen => "respond_window_open",
        };

        f.write_str(operation)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::data::{
        edit::EditAction,
        execution::BrowsingContextExecutionState,
        ids::{BrowsingContextId, TransientBrowsingContextId},
        ipc::IpcConfig,
        navigation::NavigationEntryId,
        policy::{BrowsingContextPolicy, CapabilityPolicy, IpcPolicy},
        visibility::BrowsingContextVisibility,
    };

    #[test]
    fn operation_from_command_maps_to_expected_variant() {
        let command = BrowserCommand::Navigate {
            browsing_context_id: BrowsingContextId::new(1),
            url: "https://example.com".to_string(),
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::Navigate
        );
    }

    #[test]
    fn operation_from_command_covers_navigation_history_command() {
        let command = BrowserCommand::TraverseHistoryToEntry {
            browsing_context_id: BrowsingContextId::new(7),
            entry_id: NavigationEntryId::new(99),
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::TraverseHistoryToEntry
        );
        assert_eq!(
            BrowserOperation::GetNavigationHistory.to_string(),
            "get_navigation_history"
        );
    }
    #[test]
    fn operation_from_command_covers_profile_command() {
        let command = BrowserCommand::CreateBrowsingContext {
            request_id: 42,
            initial_url: None,
            profile_id: "profile-default".to_string(),
            policy: None,
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::CreateBrowsingContext
        );
    }

    #[test]
    fn create_browsing_context_can_carry_policy() {
        let command = BrowserCommand::CreateBrowsingContext {
            request_id: 7,
            initial_url: Some("app://simpleapp/ui.html".to_string()),
            profile_id: "profile-default".to_string(),
            policy: Some(BrowsingContextPolicy {
                ipc: IpcPolicy::Allow {
                    allowed_origins: vec!["app://simpleapp".to_string()],
                },
                extensions: CapabilityPolicy::Deny,
            }),
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::CreateBrowsingContext
        );
    }

    #[test]
    fn operation_from_command_covers_transient_command() {
        let command = BrowserCommand::CloseTransientBrowsingContext {
            transient_browsing_context_id: TransientBrowsingContextId::new(8),
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::CloseTransientBrowsingContext
        );
    }

    #[test]
    fn operation_from_command_covers_close_transaction_commands() {
        let begin = BrowserCommand::BeginCloseBrowsingContextsTransaction {
            request_id: 3,
            browsing_context_ids: vec![BrowsingContextId::new(4), BrowsingContextId::new(5)],
        };
        let commit = BrowserCommand::CommitCloseBrowsingContextsTransaction { request_id: 3 };
        let cancel = BrowserCommand::CancelCloseBrowsingContextsTransaction { request_id: 3 };

        assert_eq!(
            BrowserOperation::from_command(&begin),
            BrowserOperation::BeginCloseBrowsingContextsTransaction
        );
        assert_eq!(
            BrowserOperation::BeginCloseBrowsingContextsTransaction.to_string(),
            "begin_close_browsing_contexts_transaction"
        );
        assert_eq!(
            BrowserOperation::from_command(&commit),
            BrowserOperation::CommitCloseBrowsingContextsTransaction
        );
        assert_eq!(
            BrowserOperation::CommitCloseBrowsingContextsTransaction.to_string(),
            "commit_close_browsing_contexts_transaction"
        );
        assert_eq!(
            BrowserOperation::from_command(&cancel),
            BrowserOperation::CancelCloseBrowsingContextsTransaction
        );
        assert_eq!(
            BrowserOperation::CancelCloseBrowsingContextsTransaction.to_string(),
            "cancel_close_browsing_contexts_transaction"
        );
    }

    #[test]
    fn operation_from_command_covers_edit_action_command() {
        let command = BrowserCommand::ExecuteEditAction {
            browsing_context_id: BrowsingContextId::new(5),
            action: EditAction::Copy,
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::ExecuteEditAction
        );
        assert_eq!(
            BrowserOperation::ExecuteEditAction.to_string(),
            "execute_edit_action"
        );
    }

    #[test]
    fn operation_from_command_covers_visibility_command() {
        let command = BrowserCommand::SetBrowsingContextVisibility {
            browsing_context_id: BrowsingContextId::new(5),
            visibility: BrowsingContextVisibility::Hidden,
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::SetBrowsingContextVisibility
        );
        assert_eq!(
            BrowserOperation::SetBrowsingContextVisibility.to_string(),
            "set_browsing_context_visibility"
        );
    }

    #[test]
    fn operation_from_command_covers_execution_state_command() {
        let command = BrowserCommand::SetBrowsingContextExecutionState {
            browsing_context_id: BrowsingContextId::new(6),
            state: BrowsingContextExecutionState::Suspended,
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::SetBrowsingContextExecutionState
        );
        assert_eq!(
            BrowserOperation::SetBrowsingContextExecutionState.to_string(),
            "set_browsing_context_execution_state"
        );
    }

    #[test]
    fn operation_from_command_covers_enable_ipc_command() {
        let command = BrowserCommand::EnableIpc {
            browsing_context_id: BrowsingContextId::new(7),
            config: IpcConfig {
                allowed_origins: vec!["https://example.com".to_string()],
            },
        };

        assert_eq!(
            BrowserOperation::from_command(&command),
            BrowserOperation::EnableIpc
        );
        assert_eq!(BrowserOperation::EnableIpc.to_string(), "enable_ipc");
    }
}
