/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import type { ReactNode } from 'react';
import type {
  HistoryItemWithoutId,
  HistoryItem,
  ConfirmationRequest,
} from '../types.js';
import type {
  GitService,
  Logger,
  CommandActionReturn,
  AgentDefinition,
  AgentLoopContext,
} from '@google/gemini-cli-core';
import type { LoadedSettings } from '../../config/settings.js';
import type { UseHistoryManagerReturn } from '../hooks/useHistoryManager.js';
import type { SessionStatsState } from '../contexts/SessionContext.js';
import type {
  ExtensionUpdateAction,
  ExtensionUpdateStatus,
} from '../state/extensions.js';

// Grouped dependencies for clarity and easier mocking
export interface CommandContext {
  // Invocation properties for when commands are called.
  invocation?: {
    /** The raw, untrimmed input string from the user. */
    raw: string;
    /** The primary name of the command that was matched. */
    name: string;
    /** The arguments string that follows the command name. */
    args: string;
  };
  // Core services and configuration
  services: {
    // TODO(abhipatel12): Ensure that config is never null.
    agentContext: AgentLoopContext | null;
    settings: LoadedSettings;
    git: GitService | undefined;
    logger: Logger;
  };
  // UI state and history management
  ui: {
    /** Adds a new item to the history display. */
    addItem: UseHistoryManagerReturn['addItem'];
    /** Clears all history items and the console screen. */
    clear: () => void;
    /**
     * Sets the transient debug message displayed in the application footer in debug mode.
     */
    setDebugMessage: (message: string) => void;
    /** The currently pending history item, if any. */
    pendingItem: HistoryItemWithoutId | null;
    /**
     * Sets a pending item in the history, which is useful for indicating
     * that a long-running operation is in progress.
     *
     * @param item The history item to display as pending, or `null` to clear.
     */
    setPendingItem: (item: HistoryItemWithoutId | null) => void;
    /**
     * Loads a new set of history items, replacing the current history.
     *
     * @param history The array of history items to load.
     * @param postLoadInput Optional text to set in the input buffer after loading history.
     */
    loadHistory: (history: HistoryItem[], postLoadInput?: string) => void;
    /** Toggles a special display mode. */
    toggleCorgiMode: () => void;
    toggleVoiceMode: () => void;
    toggleDebugProfiler: () => void;
    toggleVimEnabled: () => Promise<boolean>;
    reloadCommands: () => void;
    openAgentConfigDialog: (
      name: string,
      displayName: string,
      definition: AgentDefinition,
    ) => void;
    extensionsUpdateState: Map<string, ExtensionUpdateStatus>;
    dispatchExtensionStateUpdate: (action: ExtensionUpdateAction) => void;
    addConfirmUpdateExtensionRequest: (value: ConfirmationRequest) => void;
    /**
     * Sets a confirmation request to be displayed to the user.
     *
     * @param value The confirmation request details.
     */
    setConfirmationRequest: (value: ConfirmationRequest | null) => void;
    removeComponent: () => void;
    toggleBackgroundTasks: () => void;
    toggleShortcutsHelp: () => void;
  };
  // Session-specific data
  session: {
    stats: SessionStatsState;
    /** A transient list of shell commands the user has approved for this session. */
    sessionShellAllowlist: Set<string>;
  };
  // Flag to indicate if an overwrite has been confirmed
  overwriteConfirmed?: boolean;
}

/** The return type for a command action that results in the app quitting. */
export interface QuitActionReturn {
  type: 'quit';
  messages: HistoryItem[];
  /** When true, the current session's history and temporary files will be deleted on exit. */
  deleteSession?: boolean;
}

/**
 * The return type for a command action that needs to open a dialog.
 */
export interface OpenDialogActionReturn {
  type: 'dialog';
  props?: Record<string, unknown>;

  dialog:
    | 'help'
    | 'auth'
    | 'theme'
    | 'editor'
    | 'privacy'
    | 'settings'
    | 'sessionBrowser'
    | 'model'
    | 'voice-model'
    | 'agentConfig'
    | 'permissions';
}

/**
 * The return type for a command action that needs to pause and request
 * confirmation for a set of shell commands before proceeding.
 */
export interface ConfirmShellCommandsActionReturn {
  type: 'confirm_shell_commands';
  /** The list of shell commands that require user confirmation. */
  commandsToConfirm: string[];
  /** The original invocation context to be re-run after confirmation. */
  originalInvocation: {
    raw: string;
  };
}

export interface ConfirmActionReturn {
  type: 'confirm_action';
  /** The React node to display as the confirmation prompt. */
  prompt: ReactNode;
  /** The original invocation context to be re-run after confirmation. */
  originalInvocation: {
    raw: string;
  };
}

export interface OpenCustomDialogActionReturn {
  type: 'custom_dialog';
  component: ReactNode;
}

/**
 * The return type for a command action that specifically handles logout logic,
 * signaling the application to explicitly transition to an unauthenticated state.
 */
export interface LogoutActionReturn {
  type: 'logout';
}

export type SlashCommandActionReturn =
  | CommandActionReturn<HistoryItemWithoutId[]>
  | QuitActionReturn
  | OpenDialogActionReturn
  | ConfirmShellCommandsActionReturn
  | ConfirmActionReturn
  | OpenCustomDialogActionReturn
  | LogoutActionReturn;

export enum CommandKind {
  BUILT_IN = 'built-in',
  USER_FILE = 'user-file',
  WORKSPACE_FILE = 'workspace-file',
  EXTENSION_FILE = 'extension-file',
  MCP_PROMPT = 'mcp-prompt',
  AGENT = 'agent',
  SKILL = 'skill',
}

// The standardized contract for any command in the system.
export interface SlashCommand {
  name: string;
  altNames?: string[];
  description: string;
  hidden?: boolean;
  /**
   * Optional grouping label for slash completion UI sections.
   * Commands with the same label are rendered under one separator.
   */
  suggestionGroup?: string;

  kind: CommandKind;

  /**
   * Controls whether the command auto-executes when selected with Enter.
   *
   * If true, pressing Enter on the suggestion will execute the command immediately.
   * If false or undefined, pressing Enter will autocomplete the command into the prompt window.
   */
  autoExecute?: boolean;

  /**
   * Whether this command can be safely executed while the agent is busy (e.g. streaming a response).
   */
  isSafeConcurrent?: boolean;

  // Optional metadata for extension commands
  extensionName?: string;
  extensionId?: string;

  // Optional metadata for MCP commands
  mcpServerName?: string;

  // The action to run. Optional for parent commands that only group sub-commands.
  action?: (
    context: CommandContext,
    args: string, // TODO: Remove args. CommandContext now contains the complete invocation.
  ) =>
    | void
    | SlashCommandActionReturn
    | Promise<void | SlashCommandActionReturn>;

  // Provides argument completion (e.g., completing a tag for `/resume resume <tag>`).
  completion?: (
    context: CommandContext,
    partialArg: string,
  ) => Promise<string[]> | string[];

  /**
   * Whether to show the loading indicator while fetching completions.
   * Defaults to true. Set to false for fast completions to avoid flicker.
   */
  showCompletionLoading?: boolean;

  /**
   * Whether the command expects arguments.
   * If false, and the command is a subcommand, the command parser may treat
   * any following text as arguments for the parent command instead of this subcommand,
   * provided the parent command has an action.
   * Defaults to true.
   */
  takesArgs?: boolean;

  subCommands?: SlashCommand[];
}
