/**
 * TestDriver SDK - TypeScript Definitions
 */

// Type Definitions
export type ClickAction =
  | "click"
  | "right-click"
  | "double-click"
  | "hover"
  | "mouseDown"
  | "mouseUp";
export type ScrollDirection = "up" | "down" | "left" | "right";
export type ScrollMethod = "keyboard" | "mouse";
export type TextMatchMethod = "ai" | "turbo";
export type ExecLanguage = "sh" | "pwsh";
/**
 * Preview mode for live test visualization
 * - "browser": Opens debugger in default browser (default)
 * - "ide": Opens preview in IDE panel (VSCode, Cursor, etc.)
 * - "none": Headless mode, no visual preview
 */
export type PreviewMode = "browser" | "ide" | "none";
export type KeyboardKey =
  | "\t"
  | "\n"
  | "\r"
  | " "
  | "!"
  | '"'
  | "#"
  | "$"
  | "%"
  | "&"
  | "'"
  | "("
  | ")"
  | "*"
  | "+"
  | ","
  | "-"
  | "."
  | "/"
  | "0"
  | "1"
  | "2"
  | "3"
  | "4"
  | "5"
  | "6"
  | "7"
  | "8"
  | "9"
  | ":"
  | ";"
  | "<"
  | "="
  | ">"
  | "?"
  | "@"
  | "["
  | "\\"
  | "]"
  | "^"
  | "_"
  | "`"
  | "a"
  | "b"
  | "c"
  | "d"
  | "e"
  | "f"
  | "g"
  | "h"
  | "i"
  | "j"
  | "k"
  | "l"
  | "m"
  | "n"
  | "o"
  | "p"
  | "q"
  | "r"
  | "s"
  | "t"
  | "u"
  | "v"
  | "w"
  | "x"
  | "y"
  | "z"
  | "{"
  | "|"
  | "}"
  | "~"
  | "accept"
  | "add"
  | "alt"
  | "altleft"
  | "altright"
  | "apps"
  | "backspace"
  | "browserback"
  | "browserfavorites"
  | "browserforward"
  | "browserhome"
  | "browserrefresh"
  | "browsersearch"
  | "browserstop"
  | "capslock"
  | "clear"
  | "convert"
  | "ctrl"
  | "ctrlleft"
  | "ctrlright"
  | "decimal"
  | "del"
  | "delete"
  | "divide"
  | "down"
  | "end"
  | "enter"
  | "esc"
  | "escape"
  | "execute"
  | "f1"
  | "f2"
  | "f3"
  | "f4"
  | "f5"
  | "f6"
  | "f7"
  | "f8"
  | "f9"
  | "f10"
  | "f11"
  | "f12"
  | "f13"
  | "f14"
  | "f15"
  | "f16"
  | "f17"
  | "f18"
  | "f19"
  | "f20"
  | "f21"
  | "f22"
  | "f23"
  | "f24"
  | "final"
  | "fn"
  | "hanguel"
  | "hangul"
  | "hanja"
  | "help"
  | "home"
  | "insert"
  | "junja"
  | "kana"
  | "kanji"
  | "launchapp1"
  | "launchapp2"
  | "launchmail"
  | "launchmediaselect"
  | "left"
  | "modechange"
  | "multiply"
  | "nexttrack"
  | "nonconvert"
  | "num0"
  | "num1"
  | "num2"
  | "num3"
  | "num4"
  | "num5"
  | "num6"
  | "num7"
  | "num8"
  | "num9"
  | "numlock"
  | "pagedown"
  | "pageup"
  | "pause"
  | "pgdn"
  | "pgup"
  | "playpause"
  | "prevtrack"
  | "print"
  | "printscreen"
  | "prntscrn"
  | "prtsc"
  | "prtscr"
  | "return"
  | "right"
  | "scrolllock"
  | "select"
  | "separator"
  | "shift"
  | "shiftleft"
  | "shiftright"
  | "sleep"
  | "space"
  | "stop"
  | "subtract"
  | "tab"
  | "up"
  | "volumedown"
  | "volumemute"
  | "volumeup"
  | "win"
  | "winleft"
  | "winright"
  | "yen"
  | "command"
  | "option"
  | "optionleft"
  | "optionright";

export interface TestDriverOptions {
  /** API endpoint URL (default depends on release channel: latest → 'https://api.testdriver.ai') */
  apiRoot?: string;
  /** Sandbox resolution (default: '1366x768') */
  resolution?: string;
  /** Operating system for the sandbox (default: 'linux') */
  os?: "windows" | "linux";
  /** Enable analytics tracking (default: true) */
  analytics?: boolean;
  /** Enable console logging output (default: true) */
  logging?: boolean;
  /** Enable/disable cache, or configure with thresholds
   * @example { cache: { enabled: true, thresholds: { find: { screen: 0.05, element: 0.8 }, assert: 0.05 } } }
   */
  cache?: boolean | {
    enabled?: boolean;
    thresholds?: {
      /** Thresholds for find operations */
      find?: {
        /** Pixel diff threshold for screen comparison (0-1, default 0.05 = 5% diff allowed) */
        screen?: number;
        /** OpenCV template match threshold for element matching (0-1, default 0.8 = 80% correlation) */
        element?: number;
      };
      /** Pixel diff threshold for assert operations (0-1, default 0.05 = 5% diff allowed) */
      assert?: number;
    };
  };
  ai?: AIConfig;
  /** @deprecated Use cache.thresholds instead */
  cacheThreshold?: {
    /** Threshold for find operations (default: 0.05 = 5% difference, 95% similarity) */
    find?: number;
    /** Threshold for findAll operations (default: 0.05 = 5% difference, 95% similarity) */
    findAll?: number;
  };
  /** Force creation of a new sandbox (default: true) */
  newSandbox?: boolean;
  /**
   * Preview mode for live test visualization (default: "browser")
   * - "browser": Opens debugger in default browser
   * - "ide": Opens preview in IDE panel (VSCode, Cursor, etc.)
   * - "none": Headless mode, no visual preview
   */
  preview?: PreviewMode;
  /**
   * @deprecated Use `preview: "none"` instead. Run in headless mode (default: false)
   * For backward compatibility: headless: true maps to preview: "none"
   */
  headless?: boolean;
  /** Direct IP address to connect to a running sandbox instance */
  ip?: string;
  /** Custom AMI ID for sandbox instance (e.g., 'ami-1234') */
  sandboxAmi?: string;
  /** EC2 instance type for sandbox (e.g., 'i3.metal') */
  sandboxInstance?: string;
  /** E2B template ID to use when creating the sandbox (e.g., 'my-template-id') */
  e2bTemplateId?: string;
  /** Cache key for element finding operations. If provided, enables caching tied to this key */
  cacheKey?: string;
  /** Reconnect to the last used sandbox instead of creating a new one. When true, provision methods (chrome, vscode, installer, etc.) will be skipped since the application is already running. Looks up the sandbox id from `.testdriver/last-sandbox` if `sandboxId` is not also provided. */
  reconnect?: boolean;
  /** Explicit sandbox id to reconnect to. Implies `newSandbox: false` unless explicitly overridden. */
  sandboxId?: string;
  /** Enable/disable Dashcam video recording (default: true) */
  dashcam?: boolean;
  /**
   * Enable automatic screenshots before and after each command (default: false)
   * Screenshots are saved to .testdriver/screenshots/<test>/ with descriptive filenames
   * Format: <seq>-<action>-<phase>-L<line>-<description>.png
   * Example: 001-click-before-L42-submit-button.png
   */
  autoScreenshots?: boolean;
  /** Redraw configuration for screen change detection
   * @example { redraw: { enabled: true, thresholds: { screen: 0.05, network: true } } }
   */
  redraw?:
    | boolean
    | {
        /** Enable redraw detection (default: true) */
        enabled?: boolean;
        /** Threshold configuration */
        thresholds?: {
          /** Screen diff threshold (0-1). Set to false to disable screen redraw detection. Default: 0.05 */
          screen?: number | false;
          /** Enable/disable network activity monitoring (default: false) */
          network?: boolean;
        };
        /** @deprecated Use thresholds.screen instead */
        diffThreshold?: number;
        /** @deprecated Use thresholds.screen !== false instead */
        screenRedraw?: boolean;
        /** @deprecated Use thresholds.network instead */
        networkMonitor?: boolean;
      };
  /** @deprecated Use redraw option instead */
  redrawThreshold?: number | object;
  /** Additional environment variables */
  environment?: Record<string, any>;
}

export interface ConnectOptions {
  /** Existing sandbox ID to reconnect to */
  sandboxId?: string;
  /** Force creation of a new sandbox */
  newSandbox?: boolean;
  /** Reconnect to the last used sandbox instead of creating a new one. When true, provision methods (chrome, vscode, installer, etc.) will be skipped since the application is already running. Throws error if no previous sandbox exists. */
  reconnect?: boolean;
  /** Direct IP address to connect to a running sandbox instance */
  ip?: string;
  /** Custom AMI ID for sandbox instance (e.g., 'ami-1234') */
  sandboxAmi?: string;
  /** EC2 instance type for sandbox (e.g., 'i3.metal') */
  sandboxInstance?: string;
  /** E2B template ID to use when creating the sandbox (e.g., 'my-template-id') */
  e2bTemplateId?: string;
  /** Operating system for the sandbox (default: 'linux') */
  os?: "windows" | "linux";
  /**
   * Preview mode for live test visualization (default: "browser")
   * - "browser": Opens debugger in default browser
   * - "ide": Opens preview in IDE panel (VSCode, Cursor, etc.)
   * - "none": Headless mode, no visual preview
   */
  preview?: PreviewMode;
  /**
   * @deprecated Use `preview: "none"` instead. Run in headless mode (default: false)
   * For backward compatibility: headless: true maps to preview: "none"
   */
  headless?: boolean;
  /** Reuse recent connection if available (default: true) */
  reuseConnection?: boolean;
  /** Keep sandbox alive for specified milliseconds after disconnect (default: 60000). Set to 0 to terminate immediately on disconnect. */
  keepAlive?: number;
}

export interface SandboxInstance {
  instanceId: string;
  ip: string;
  vncPort: number;
  [key: string]: any;
}

export interface ElementCoordinates {
  x: number;
  y: number;
  centerX: number;
  centerY: number;
}

export interface ElementBoundingBox {
  top: number;
  left: number;
  bottom: number;
  right: number;
  [key: string]: any;
}

export interface ElementResponse {
  coordinates: ElementCoordinates;
  confidence?: number;
  screenshot?: string;
  width?: number;
  height?: number;
  boundingBox?: ElementBoundingBox;
  text?: string;
  label?: string;
  [key: string]: any;
}

export interface HoverResult {
  x: number;
  y: number;
  centerX: number;
  centerY: number;
  [key: string]: any;
}

/** Bounding box for a parsed element (pixel coordinates) */
export interface ParsedElementBBox {
  /** Left edge X coordinate */
  x0: number;
  /** Top edge Y coordinate */
  y0: number;
  /** Right edge X coordinate */
  x1: number;
  /** Bottom edge Y coordinate */
  y1: number;
}

/** Bounding box as {left, top, width, height} */
export interface ParsedElementBoundingBox {
  left: number;
  top: number;
  width: number;
  height: number;
}

/** Individual element detected by OmniParser */
export interface ParsedElement {
  /** Element index */
  index: number;
  /** Element type (e.g. "text", "icon", "button") */
  type: string;
  /** Text content or description */
  content: string;
  /** Interactivity level (e.g. "clickable", "non-interactive") */
  interactivity: string;
  /** Bounding box in pixel coordinates */
  bbox: ParsedElementBBox;
  /** Bounding box as {left, top, width, height} */
  boundingBox: ParsedElementBoundingBox;
}

/** Result from OmniParser screen analysis */
export interface ParseResult {
  /** Array of detected UI elements */
  elements: ParsedElement[];
  /** URL of the annotated screenshot */
  annotatedImageUrl: string;
  /** Width of the analyzed screenshot */
  imageWidth: number;
  /** Height of the analyzed screenshot */
  imageHeight: number;
}

// ====================================
// Command Options Interfaces
// ====================================

/** Options for scroll command */
export interface ScrollOptions {
  /** Direction to scroll */
  direction?: ScrollDirection;
  /** Amount to scroll in pixels */
  amount?: number;
}

/** Options for click command */
export interface ClickOptions {
  /** X coordinate */
  x: number;
  /** Y coordinate */
  y: number;
  /** Type of click action */
  action?: ClickAction;
  /** Prompt for tracking */
  prompt?: string;
  /** Whether cache was hit */
  cacheHit?: boolean;
  /** Selector used */
  selector?: string;
  /** Whether selector was used */
  selectorUsed?: boolean;
}

/** Options for hover command */
export interface HoverOptions {
  /** X coordinate */
  x: number;
  /** Y coordinate */
  y: number;
  /** Prompt for tracking */
  prompt?: string;
  /** Whether cache was hit */
  cacheHit?: boolean;
  /** Selector used */
  selector?: string;
  /** Whether selector was used */
  selectorUsed?: boolean;
}

/** Options for hoverText command */
export interface HoverTextOptions {
  /** Text to find and hover over */
  text: string;
  /** Optional description of the element */
  description?: string | null;
  /** Action to perform */
  action?: ClickAction;
  /** Timeout in milliseconds */
  timeout?: number;
}

/** Options for hoverImage command */
export interface HoverImageOptions {
  /** Description of the image to find */
  description: string;
  /** Action to perform */
  action?: ClickAction;
}

/** Options for matchImage command */
export interface MatchImageOptions {
  /** Path to the image template */
  path: string;
  /** Action to perform */
  action?: ClickAction;
  /** Invert the match */
  invert?: boolean;
}

/** Options for type command */
export interface TypeOptions {
  /** Text to type */
  text: string | number;
  /** Delay between keystrokes in milliseconds */
  delay?: number;
}

/** Options for pressKeys command */
export interface PressKeysOptions {
  /** Array of keys to press */
  keys: KeyboardKey[];
}

/** Options for wait command */
export interface WaitOptions {
  /** Time to wait in milliseconds */
  timeout?: number;
}

/** Options for waitForText command */
export interface WaitForTextOptions {
  /** Text to wait for */
  text: string;
  /** Timeout in milliseconds */
  timeout?: number;
}

/** Options for waitForImage command */
export interface WaitForImageOptions {
  /** Description of the image */
  description: string;
  /** Timeout in milliseconds */
  timeout?: number;
}

/** Options for scrollUntilText command */
export interface ScrollUntilTextOptions {
  /** Text to find */
  text: string;
  /** Scroll direction */
  direction?: ScrollDirection;
  /** Maximum distance to scroll in pixels */
  maxDistance?: number;
  /** Invert the match */
  invert?: boolean;
}

/** Options for scrollUntilImage command */
export interface ScrollUntilImageOptions {
  /** Description of the image */
  description?: string;
  /** Scroll direction */
  direction?: ScrollDirection;
  /** Maximum distance to scroll in pixels */
  maxDistance?: number;
  /** Scroll method */
  method?: ScrollMethod;
  /** Path to image template */
  path?: string;
  /** Invert the match */
  invert?: boolean;
}

/** Options for focusApplication command */
export interface FocusApplicationOptions {
  /** Application name */
  name: string;
}

/** AI sampling configuration for controlling model behavior */
export interface AIConfig {
  /** Temperature for AI sampling (0 = deterministic, higher = more creative). Default: 0 for find verification, model default for assert. */
  temperature?: number;
  /** Top-P and Top-K sampling parameters */
  top?: {
    /** Top-P (nucleus sampling). Controls diversity by limiting to top P probability mass. Range: 0-1. */
    p?: number;
    /** Top-K sampling. Limits choices to top K tokens. 1 = always pick most likely. 0 = disabled. */
    k?: number;
  };
}

/** Options for extract command */
export interface ExtractOptions {
  /** What to extract */
  description: string;
}

/** Options for assert command */
export interface AssertOptions {
  /** Assertion to check */
  assertion: string;
  /** Cache threshold (0-1). Lower values require closer matches. Set to -1 to disable cache. */
  threshold?: number;
  /** Cache key for grouping cached assertions (enables caching when provided) */
  cacheKey?: string;
  /** Operating system identifier for cache partitioning */
  os?: string;
  /** Screen resolution for cache partitioning */
  resolution?: string;
  /** AI sampling configuration (overrides global ai config) */
  ai?: AIConfig;
}

/** Options for exec command */
export interface ExecOptions {
  /** Language ('js', 'pwsh', or 'sh') */
  language?: ExecLanguage;
  /** Code to execute */
  code: string;
  /** Timeout in milliseconds */
  timeout?: number;
  /** Suppress output */
  silent?: boolean;
}

/** Options for captcha command */
export interface CaptchaOptions {
  /** 2captcha API key (required) */
  apiKey: string;
  /** Override auto-detected sitekey */
  sitekey?: string;
  /** Captcha type: 'recaptcha_v2', 'recaptcha_v3', 'hcaptcha', 'turnstile' */
  type?: string;
  /** reCAPTCHA v3 action (default: 'verify') */
  action?: string;
  /** Whether to auto-submit the form (default: true) */
  autoSubmit?: boolean;
  /** Polling interval in ms for 2captcha (default: 5000) */
  pollInterval?: number;
  /** Max time in ms to wait for solution (default: 120000) */
  timeout?: number;
}

/** Result of captcha solving */
export interface CaptchaResult {
  /** Whether the captcha was solved successfully */
  success: boolean;
  /** Success/error message */
  message: string;
  /** The solved captcha token */
  token: string | null;
  /** Raw output from the solver script */
  output: string;
}

/**
 * A Promise that resolves to an Element but also has chainable element methods.
 * This enables syntax like: await testdriver.find("button").click()
 */
export interface ChainableElementPromise extends Promise<Element> {
  /**
   * Click on the element (chainable)
   * @param action - Type of click action (default: 'click')
   */
  click(action?: ClickAction): Promise<void>;

  /**
   * Hover over the element (chainable)
   */
  hover(): Promise<void>;

  /**
   * Double-click on the element (chainable)
   */
  doubleClick(): Promise<void>;

  /**
   * Right-click on the element (chainable)
   */
  rightClick(): Promise<void>;

  /**
   * Press mouse button down on this element (chainable)
   */
  mouseDown(): Promise<void>;

  /**
   * Release mouse button on this element (chainable)
   */
  mouseUp(): Promise<void>;

  /**
   * Check if element was found (chainable)
   */
  found(): Promise<boolean>;

  /**
   * Get the coordinates of the element (chainable)
   */
  getCoordinates(): Promise<ElementCoordinates | null>;

  /**
   * Get the full API response data (chainable)
   */
  getResponse(): Promise<ElementResponse | null>;

  /**
   * Get the x coordinate (chainable)
   */
  readonly x: Promise<number | null>;

  /**
   * Get the y coordinate (chainable)
   */
  readonly y: Promise<number | null>;

  /**
   * Get the center x coordinate (chainable)
   */
  readonly centerX: Promise<number | null>;

  /**
   * Get the center y coordinate (chainable)
   */
  readonly centerY: Promise<number | null>;
}

/**
 * Element class representing a located or to-be-located element on screen
 */
export class Element {
  constructor(description: string);

  /**
   * Check if element was found
   */
  found(): boolean;

  /**
   * Find the element on screen
   * @param newDescription - Optional new description to search for
   * @param options - Cache options: number for threshold, or object with cache.thresholds
   */
  find(newDescription?: string, cacheThreshold?: number): Promise<Element>;

  /**
   * Click on the element
   * @param action - Type of click action (default: 'click')
   */
  click(action?: ClickAction): Promise<void>;

  /**
   * Hover over the element
   */
  hover(): Promise<void>;

  /**
   * Double-click on the element
   */
  doubleClick(): Promise<void>;

  /**
   * Right-click on the element
   */
  rightClick(): Promise<void>;

  /**
   * Press mouse button down on this element
   */
  mouseDown(): Promise<void>;

  /**
   * Release mouse button on this element
   */
  mouseUp(): Promise<void>;

  /**
   * Get the coordinates of the element
   */
  getCoordinates(): ElementCoordinates | null;

  /**
   * Get the x coordinate (top-left)
   */
  readonly x: number | null;

  /**
   * Get the y coordinate (top-left)
   */
  readonly y: number | null;

  /**
   * Get the center x coordinate
   */
  readonly centerX: number | null;

  /**
   * Get the center y coordinate
   */
  readonly centerY: number | null;

  /**
   * Get the full API response data
   */
  getResponse(): ElementResponse | null;

  /**
   * Get element screenshot if available (base64 encoded)
   */
  readonly screenshot: string | null;

  /**
   * Get element confidence score if available
   */
  readonly confidence: number | null;

  /**
   * Get element width if available
   */
  readonly width: number | null;

  /**
   * Get element height if available
   */
  readonly height: number | null;

  /**
   * Get element bounding box if available
   */
  readonly boundingBox: ElementBoundingBox | null;

  /**
   * Get element text content if available
   */
  readonly text: string | null;

  /**
   * Get element label if available
   */
  readonly label: string | null;
}

// ====================================
// Provision API Interfaces
// ====================================

/** Options for provision.chrome */
export interface ProvisionChromeOptions {
  /** URL to navigate to (default: 'http://testdriver-sandbox.vercel.app/') */
  url?: string;
  /** Start maximized (default: true) */
  maximized?: boolean;
  /** Use guest mode (default: false) */
  guest?: boolean;
}

/** Options for provision.chromeExtension */
export interface ProvisionChromeExtensionOptions {
  /** Local filesystem path to the unpacked extension directory */
  extensionPath?: string;
  /** Chrome Web Store extension ID */
  extensionId?: string;
  /** Start maximized (default: true) */
  maximized?: boolean;
}

/** Options for provision.vscode */
export interface ProvisionVSCodeOptions {
  /** Path to workspace or folder to open */
  workspace?: string;
  /** Array of extension IDs to install */
  extensions?: string[];
}

/** Options for provision.installer */
export interface ProvisionInstallerOptions {
  /** URL to download the installer from */
  url: string;
  /** Filename to save as (auto-detected from URL if not provided) */
  filename?: string;
  /** Application name to focus after install */
  appName?: string;
  /** Whether to launch the app after installation (default: true) */
  launch?: boolean;
}

/** Options for provision.electron */
export interface ProvisionElectronOptions {
  /** Path to Electron app (required) */
  appPath: string;
  /** Additional electron args */
  args?: string[];
}

/** Options for provision.dashcam */
export interface ProvisionDashcamOptions {
  /** Path to log file (auto-generated if not provided) */
  logPath?: string;
  /** Display name for the log (default: 'TestDriver Log') */
  logName?: string;
  /** Enable web log tracking (default: true) */
  webLogs?: boolean;
  /** Custom title for the recording */
  title?: string;
}

/** Provision API for launching applications */
export interface ProvisionAPI {
  /**
   * Launch Chrome browser
   * @param options - Chrome launch options
   */
  chrome(options?: ProvisionChromeOptions): Promise<void>;

  /**
   * Launch Chrome browser with a custom extension loaded
   * @param options - Chrome extension launch options
   */
  chromeExtension(options?: ProvisionChromeExtensionOptions): Promise<void>;

  /**
   * Launch VS Code
   * @param options - VS Code launch options
   */
  vscode(options?: ProvisionVSCodeOptions): Promise<void>;

  /**
   * Download and install an application
   * @param options - Installer options
   * @returns Path to the downloaded file
   */
  installer(options: ProvisionInstallerOptions): Promise<string>;

  /**
   * Launch Electron app
   * @param options - Electron launch options
   */
  electron(options: ProvisionElectronOptions): Promise<void>;

  /**
   * Initialize Dashcam recording with logging
   * @param options - Dashcam options
   */
  dashcam(options?: ProvisionDashcamOptions): Promise<void>;
}

/** Dashcam API for screen recording */
export interface DashcamAPI {
  /**
   * Start recording
   */
  start(): Promise<void>;

  /**
   * Stop recording and get replay URL
   */
  stop(): Promise<string | null>;

  /**
   * Check if currently recording
   */
  isRecording(): boolean;
}

export default class TestDriverSDK {
  /**
   * Create a new TestDriverSDK instance
   * Automatically loads environment variables from .env file via dotenv.
   * 
   * @param apiKey - API key (optional, defaults to TD_API_KEY environment variable)
   * @param options - SDK configuration options
   * 
   * @example
   * // API key loaded automatically from TD_API_KEY in .env
   * const client = new TestDriver();
   * 
   * @example
   * // Pass options only (API key from .env)
   * const client = new TestDriver({ os: 'windows' });
   * 
   * @example
   * // Or pass API key explicitly
   * const client = new TestDriver('your-api-key');
   */
  constructor(apiKey?: string | TestDriverOptions, options?: TestDriverOptions);

  /**
   * Whether the SDK is currently connected to a sandbox
   */
  readonly connected: boolean;

  /**
   * The operating system of the sandbox
   */
  readonly os: "windows" | "linux";

  /**
   * Provision API for launching applications
   */
  readonly provision: ProvisionAPI;

  /**
   * Dashcam API for screen recording
   */
  readonly dashcam: DashcamAPI;

  /**
   * Whether Dashcam recording is enabled (default: true)
   */
  readonly dashcamEnabled: boolean;

  /**
   * Wait for the sandbox to be ready
   * Called automatically by provision methods
   */
  ready(): Promise<void>;

  /**
   * Authenticate with TestDriver API
   */
  auth(): Promise<string>;

  /**
   * Connect to a sandbox environment
   */
  connect(options?: ConnectOptions): Promise<SandboxInstance>;

  /**
   * Disconnect from the sandbox
   */
  disconnect(): Promise<void>;

  /**
   * Get info about the most recently provisioned sandbox (from this SDK instance
   * if connected, otherwise read from `.testdriver/last-sandbox`).
   * @returns Last sandbox info or null if not found
   */
  getLastSandboxId(): {
    sandboxId: string;
    os?: "windows" | "linux";
    e2bTemplateId?: string | null;
    createdAt?: number;
  } | null;

  // Element Finding API

  /**
   * Find an element by description
   * Automatically locates the element and returns it
   *
   * @param description - Description of the element to find
   * @param options - Cache threshold (number) or options object with cache.thresholds
   * @returns Chainable promise that resolves to Element instance
   *
   * @example
   * // Find and click immediately (chainable)
   * await client.find('the sign in button').click();
   *
   * @example
   * // Find and click (traditional)
   * const element = await client.find('the sign in button');
   * await element.click();
   *
   * @example
   * // Find with custom cache thresholds
   * const element = await client.find('login button', {
   *   cache: { thresholds: { screen: 0.05, element: 0.9 } }
   * });
   *
   * @example
   * // Poll for element with timeout (retries every 5 seconds)
   * const element = await client.find('loading complete indicator', { timeout: 30000 });
   * await element.click();
   */
  find(description: string, cacheThreshold?: number): ChainableElementPromise;
  find(
    description: string,
    options?: { cacheThreshold?: number; cacheKey?: string; timeout?: number; confidence?: number; type?: "text" | "image" | "ui" | "any"; zoom?: boolean | number; verify?: boolean; ai?: AIConfig; cache?: { thresholds?: { screen?: number; element?: number } } },
  ): ChainableElementPromise;

  /**
   * Find all elements matching a description
   * @param description - Description of the elements to find
   * @param cacheThreshold - Cache threshold for this specific findAll (overrides global setting)
   * @returns Array of Element instances
   *
   * @example
   * // Find all buttons
   * const buttons = await client.findAll('button');
   *
   * @example
   * // Find with custom cache threshold
   * const items = await client.findAll('list item', 0.05);
   */
  findAll(description: string, cacheThreshold?: number): Promise<Element[]>;
  findAll(
    description: string,
    options?: { cacheThreshold?: number; cacheKey?: string; cache?: { thresholds?: { screen?: number } } },
  ): Promise<Element[]>;

  // Text Interaction Methods

  /**
   * Hover over text on screen
   * @deprecated Use find() and element.click() instead
   * @param options - Options object with text, description, action, and timeout
   */
  hoverText(options: HoverTextOptions): Promise<HoverResult>;
  /**
   * Hover over text on screen (positional arguments - legacy)
   * @deprecated Use find() and element.click() instead
   * @param text - Text to find and hover over
   * @param description - Optional description of the element
   * @param action - Action to perform (default: 'click')
   * @param timeout - Timeout in milliseconds (default: 5000)
   */
  hoverText(
    text: string,
    description?: string | null,
    action?: ClickAction,
    timeout?: number,
  ): Promise<HoverResult>;

  /**
   * Type text
   * @param text - Text to type
   * @param options - Options object with delay and secret
   *
   * @example
   * // Type regular text
   * await client.type('hello world');
   *
   * @example
   * // Type a password securely (not logged or stored)
   * await client.type(process.env.TD_PASSWORD, { secret: true });
   *
   * @example
   * // Type with custom delay
   * await client.type('slow typing', { delay: 100 });
   */
  type(
    text: string | number,
    options?: { delay?: number; secret?: boolean },
  ): Promise<void>;

  /**
   * Wait for text to appear on screen
   * @deprecated Use find() in a polling loop instead
   * @param options - Options object with text and timeout
   */
  waitForText(options: WaitForTextOptions): Promise<void>;
  /**
   * Wait for text to appear on screen (positional arguments - legacy)
   * @deprecated Use find() in a polling loop instead
   * @param text - Text to wait for
   * @param timeout - Timeout in milliseconds (default: 5000)
   */
  waitForText(text: string, timeout?: number): Promise<void>;

  /**
   * Scroll until text is found
   * @param options - Options object with text, direction, maxDistance, and invert
   */
  scrollUntilText(options: ScrollUntilTextOptions): Promise<void>;
  /**
   * Scroll until text is found (positional arguments - legacy)
   * @param text - Text to find
   * @param direction - Scroll direction (default: 'down')
   * @param maxDistance - Maximum distance to scroll in pixels (default: 10000)
   * @param invert - Invert the match (default: false)
   */
  scrollUntilText(
    text: string,
    direction?: ScrollDirection,
    maxDistance?: number,
    invert?: boolean,
  ): Promise<void>;

  // Image Interaction Methods

  /**
   * Hover over an image on screen
   * @deprecated Use find() and element.click() instead
   * @param options - Options object with description and action
   */
  hoverImage(options: HoverImageOptions): Promise<HoverResult>;
  /**
   * Hover over an image on screen (positional arguments - legacy)
   * @deprecated Use find() and element.click() instead
   * @param description - Description of the image to find
   * @param action - Action to perform (default: 'click')
   */
  hoverImage(description: string, action?: ClickAction): Promise<HoverResult>;

  /**
   * Match and interact with an image template
   * @param options - Options object with path, action, and invert
   */
  matchImage(options: MatchImageOptions): Promise<boolean>;
  /**
   * Match and interact with an image template (positional arguments - legacy)
   * @param imagePath - Path to the image template
   * @param action - Action to perform (default: 'click')
   * @param invert - Invert the match (default: false)
   */
  matchImage(
    imagePath: string,
    action?: ClickAction,
    invert?: boolean,
  ): Promise<boolean>;

  /**
   * Wait for image to appear on screen
   * @deprecated Use find() in a polling loop instead
   * @param options - Options object with description and timeout
   */
  waitForImage(options: WaitForImageOptions): Promise<void>;
  /**
   * Wait for image to appear on screen (positional arguments - legacy)
   * @deprecated Use find() in a polling loop instead
   * @param description - Description of the image
   * @param timeout - Timeout in milliseconds (default: 10000)
   */
  waitForImage(description: string, timeout?: number): Promise<void>;

  /**
   * Scroll until image is found
   * @param options - Options object with description, direction, maxDistance, method, path, and invert
   */
  scrollUntilImage(options: ScrollUntilImageOptions): Promise<void>;
  /**
   * Scroll until image is found (positional arguments - legacy)
   * @param description - Description of the image (or use path parameter)
   * @param direction - Scroll direction (default: 'down')
   * @param maxDistance - Maximum distance to scroll in pixels (default: 10000)
   * @param method - Scroll method (default: 'keyboard')
   * @param path - Path to image template (default: null)
   * @param invert - Invert the match (default: false)
   */
  scrollUntilImage(
    description: string,
    direction?: ScrollDirection,
    maxDistance?: number,
    method?: ScrollMethod,
    path?: string | null,
    invert?: boolean,
  ): Promise<void>;

  // Mouse & Keyboard Methods

  /**
   * Click at coordinates
   * @param options - Options object with x, y, and action
   */
  click(options: ClickOptions): Promise<void>;
  /**
   * Click at coordinates (positional arguments - legacy)
   * @param x - X coordinate
   * @param y - Y coordinate
   * @param action - Type of click action (default: 'click')
   */
  click(x: number, y: number, action?: ClickAction): Promise<void>;

  /**
   * Hover at coordinates
   * @param options - Options object with x and y
   */
  hover(options: HoverOptions): Promise<void>;
  /**
   * Hover at coordinates (positional arguments - legacy)
   * @param x - X coordinate
   * @param y - Y coordinate
   */
  hover(x: number, y: number): Promise<void>;

  /**
   * Press keyboard keys
   * @param keys - Array of keys to press
   * @param options - Additional options (reserved for future use)
   */
  pressKeys(keys: KeyboardKey[], options?: object): Promise<void>;

  /**
   * Scroll the page
   * @param direction - Direction to scroll (default: 'down')
   * @param options - Options object with amount
   */
  scroll(
    direction?: ScrollDirection,
    options?: { amount?: number },
  ): Promise<void>;

  // Application Control

  /**
   * Focus an application by name
   * @param name - Application name
   * @param options - Additional options (reserved for future use)
   */
  focusApplication(name: string, options?: object): Promise<string>;

  // AI-Powered Methods

  /**
   * Make an AI-powered assertion
   * @param assertion - Assertion to check
   * @param options - Cache options for the assertion
   *
   * @example
   * // Simple assertion
   * await client.assert('the login form is visible');
   *
   * @example
   * // With caching enabled via cacheKey
   * await client.assert('the submit button is enabled', { cacheKey: 'my-test-run' });
   *
   * @example
   * // With custom threshold
   * await client.assert('the page loaded', { threshold: 0.05, cacheKey: 'login-test' });
   */
  assert(assertion: string, options?: { threshold?: number; cacheKey?: string; os?: string; resolution?: string; ai?: AIConfig }): Promise<boolean>;

  /**
   * Extract information from the screen using AI
   * @param options - Options object with description
   */
  extract(options: { description: string }): Promise<string>;
  /**
   * Extract information from the screen using AI (positional arguments - legacy)
   * @param description - What to extract
   */
  extract(description: string): Promise<string>;

  /**
   * Solve a captcha on the current page using 2captcha service
   * @param options - Captcha solving options
   */
  captcha(options: CaptchaOptions): Promise<CaptchaResult>;

  // Code Execution

  /**
   * Execute code in the sandbox
   * @param options - Options object with language, code, timeout, and silent
   */
  exec(options: ExecOptions): Promise<string>;
  /**
   * Execute code in the sandbox (positional arguments - legacy)
   * @param language - Language ('js' or 'pwsh')
   * @param code - Code to execute
   * @param timeout - Timeout in milliseconds
   * @param silent - Suppress output (default: false)
   */
  exec(
    language: ExecLanguage,
    code: string,
    timeout: number,
    silent?: boolean,
  ): Promise<string>;

  // Utility Methods

  /**
   * Capture a screenshot of the current screen and save it to .testdriver/screenshots
   * @param filename - Custom filename (without .png extension)
   * @returns The file path where the screenshot was saved
   *
   * @example
   * // Capture a screenshot with auto-generated filename
   * const screenshotPath = await testdriver.screenshot();
   *
   * @example
   * // Capture with custom filename
   * const screenshotPath = await testdriver.screenshot("login-page");
   * // Saves to: .testdriver/screenshots/<test>/login-page.png
   */
  screenshot(filename?: string): Promise<string>;

  /**
   * Parse the current screen using OmniParser v2 to detect all UI elements
   * Returns structured data with element types, bounding boxes, and content
   * Requires enterprise or self-hosted plan.
   *
   * @returns Parsed screen elements with positions and types
   *
   * @example
   * // Get all elements on screen
   * const result = await testdriver.parse();
   * console.log(`Found ${result.elements.length} elements`);
   *
   * @example
   * // Find clickable elements
   * const result = await testdriver.parse();
   * const clickable = result.elements.filter(e => e.interactivity === 'clickable');
   *
   * @example
   * // Find text content
   * const result = await testdriver.parse();
   * const textElements = result.elements.filter(e => e.type === 'text');
   */
  parse(): Promise<ParseResult>;

  /**
   * Wait for specified time. Useful for adding delays between actions, waiting for
   * animations to complete, or pausing for state changes to settle.
   *
   * For waiting for specific elements to appear, prefer `find()` with a `timeout` option instead.
   *
   * @param timeout - Time to wait in milliseconds (default: 3000)
   * @param options - Additional options (reserved for future use)
   *
   * @example
   * // Wait 2 seconds for an animation to complete
   * await testdriver.wait(2000);
   *
   * @example
   * // Wait after a click for state to settle
   * await testdriver.find('submit button').click();
   * await testdriver.wait(1000);
   */
  wait(timeout?: number, options?: object): Promise<void>;

  /**
   * Get the current sandbox instance details
   */
  getInstance(): SandboxInstance | null;

  /**
   * Get the session ID
   */
  getSessionId(): string | null;

  /**
   * Enable or disable logging output
   */
  setLogging(enabled: boolean): void;

  /**
   * Get the event emitter for custom event handling
   */
  getEmitter(): any; // EventEmitter2 type

  // AI Methods (Exploratory Loop)

  /**
   * @deprecated Use ai() instead
   * Execute a natural language task using AI
   * This is the SDK equivalent of the CLI's exploratory loop
   *
   * @param task - Natural language description of what to do
   * @param options - Execution options
   * @returns Final AI response if validateAndLoop is true
   *
   * @example
   * // Simple execution
   * await client.ai('Click the submit button');
   *
   * @example
   * // With validation loop
   * const result = await client.ai('Fill out the contact form', { validateAndLoop: true });
   * console.log(result); // AI's final assessment
   */
  act(
    task: string,
    options?: { validateAndLoop?: boolean },
  ): Promise<string | void>;

  /**
   * Execute a natural language task using AI
   *
   * @param task - Natural language description of what to do
   * @param options - Execution options
   * @returns Final AI response if validateAndLoop is true
   */
  ai(
    task: string,
    options?: { validateAndLoop?: boolean },
  ): Promise<string | void>;
}
