import { AbstractInterface } from '@midscene/core/device';
import { Agent } from '@midscene/core/agent';
import { AgentBehaviorInitArgs } from '@midscene/shared/agent-tools/agent-behavior-init-args';
import { AgentOpt } from '@midscene/core/agent';
import { BaseMidsceneTools } from '@midscene/shared/agent-tools/base-tools';
import { ChildProcessWithoutNullStreams } from 'node:child_process';
import { ComputerInputPrimitives } from '@midscene/core/device';
import type { DeviceAction } from '@midscene/core';
import { InitArgSpec } from '@midscene/shared/agent-tools/base-tools';
import { InputStrategy } from '@midscene/core/device';
import type { InterfaceType } from '@midscene/core';
import { overrideAIConfig } from '@midscene/shared/env';
import type { Size } from '@midscene/core';
import { SpawnOptionsWithoutStdio } from 'node:child_process';
import type { ToolDefinition } from '@midscene/shared/agent-tools/types';

declare interface AccessibilityCheckResult {
    hasPermission: boolean;
    platform: string;
    error?: string;
}

export declare function agentForComputer(opts?: LocalComputerAgentOpt): Promise<ComputerAgent<ComputerDevice>>;

export declare function agentForRDPComputer(opts: RDPComputerAgentOpt): Promise<ComputerAgent<RDPDevice>>;

/**
 * @deprecated Use `agentForComputer` instead.
 */
export declare const agentFromComputer: typeof agentForComputer;

export declare type BaseComputerAgentOpt = AgentOpt & ComputerAgentSharedDeviceOpt;

/**
 * Check if macOS accessibility permission is granted
 * On other platforms, always returns true
 *
 * @param promptIfNeeded - If true, will trigger system prompt and open settings when permission is not granted (macOS only)
 */
export declare function checkAccessibilityPermission(promptIfNeeded?: boolean): AccessibilityCheckResult;

/**
 * Check if the computer environment is available
 */
export declare function checkComputerEnvironment(): Promise<EnvironmentCheck>;

/**
 * Check if macOS Screen Recording permission is granted (required for screencapture).
 * Accessibility and Screen Recording are independent TCC permissions — granting
 * one does not grant the other. On non-macOS platforms, always returns true.
 *
 * @param promptIfNeeded - If true, will trigger system prompt and open settings when permission is not granted (macOS only)
 */
export declare function checkScreenRecordingPermission(promptIfNeeded?: boolean): AccessibilityCheckResult;

/**
 * Check if Xvfb is installed on the system
 */
export declare function checkXvfbInstalled(): boolean;

export declare class ComputerAgent<InterfaceType extends AbstractInterface = ComputerInterface> extends Agent<InterfaceType> {
}

export declare type ComputerAgentOpt = LocalComputerAgentOpt;

declare type ComputerAgentSharedDeviceOpt = Pick<ComputerDeviceOpt, 'customActions' | 'inputStrategy' | 'keyboardTypeDelay'>;

export declare class ComputerDevice implements AbstractInterface {
    interfaceType: InterfaceType;
    private options?;
    private displayId?;
    private displayGeometry?;
    private description?;
    private destroyed;
    private xvfbInstance?;
    private xvfbCleanup?;
    private xvfbSignalCleanup?;
    private readonly inputDriver;
    private readonly windowsPointerDriver;
    /**
     * On macOS, use AppleScript for keyboard operations by default
     * to avoid focus issues with system overlays (e.g. Spotlight).
     */
    private useAppleScript;
    /** Cached result of the elevation check; see isRunningAsAdmin(). */
    private adminCheckCache?;
    uri?: string;
    readonly inputPrimitives: ComputerInputPrimitives;
    constructor(options?: ComputerDeviceOpt);
    private moveGlobalPointer;
    private moveDisplayPointer;
    private focusKeyboardTarget;
    describe(): string;
    /**
     * Get all available displays
     */
    static listDisplays(): Promise<DisplayInfo[]>;
    connect(): Promise<void>;
    private healthCheck;
    /**
     * Check if the current process is running with Administrator privileges.
     * Uses "net session" which succeeds only when elevated.
     *
     * The result is cached because elevation cannot change during the process
     * lifetime, and the underlying `execSync('net session')` is a blocking
     * subprocess spawn that should not run on every connect / health check.
     */
    private isRunningAsAdmin;
    screenshotBase64(): Promise<string>;
    /**
     * Windows screenshot path that bypasses screenshot-desktop's polyglot .bat
     * (see screenshotBase64 for the rationale). Captures via PowerShell +
     * System.Drawing, which honors `displayId` by matching the monitor's
     * DeviceName and captures in virtual-desktop coordinates, so secondary
     * displays — including those at negative offsets — are supported.
     *
     * The PowerShell thread is switched to Per-Monitor V2 before WinForms is
     * loaded. Screen.Bounds, CopyFromScreen, and pointer movement therefore use
     * physical pixels even when Windows display scaling is enabled. The native
     * declaration is emitted in memory, so this does not require csc.exe.
     */
    private screenshotViaPowershell;
    size(): Promise<Size>;
    private toGlobalPoint;
    /**
     * Type text via clipboard (paste)
     * This method:
     * 1. Saves the old clipboard content
     * 2. Writes new content to clipboard
     * 3. Simulates paste shortcut (Ctrl+V / Cmd+V)
     * 4. Restores old clipboard content
     */
    private typeViaClipboard;
    /**
     * Keep clipboard paste as the default to avoid IME interference. A positive
     * delay explicitly opts into real key events, which can be required by apps
     * that reject paste or need key-by-key input.
     */
    private smartTypeString;
    private typeStringWithDelay;
    private selectAllAndDelete;
    private pressKeyboardShortcut;
    private resolveUntargetedScrollPoint;
    private moveMouseToScrollTarget;
    private performScroll;
    actionSpace(): DeviceAction<any>[];
    destroy(): Promise<void>;
    url(): Promise<string>;
}

declare interface ComputerDeviceInputOpt {
    /**
     * Delay in milliseconds between keystrokes when typing text.
     *
     * Must be a finite non-negative number. In `legacy` mode, positive values
     * switch input from clipboard paste to real key events. This helps
     * applications that require physical-looking keystrokes or drop characters
     * when an entire value arrives at once. When omitted or set to zero,
     * `legacy` mode keeps using clipboard paste to avoid IME interference.
     */
    keyboardTypeDelay?: number;
    /**
     * How Midscene sends text to the desktop input backend. `bulk` uses one
     * clipboard paste; `sequential` emits one Unicode code point at a time.
     * `bulk` requires `keyboardTypeDelay` to be omitted or set to zero.
     * @default 'legacy'
     */
    inputStrategy?: InputStrategy;
}

export declare interface ComputerDeviceOpt extends ComputerDeviceInputOpt {
    displayId?: string;
    customActions?: DeviceAction<any>[];
    /**
     * Keyboard driver for sending key events (macOS only)
     * - 'applescript': Use AppleScript via osascript (default on macOS, more reliable)
     * - 'libnut': Use libnut's keyTap (faster but may not work with some TUI apps)
     */
    keyboardDriver?: 'applescript' | 'libnut';
    /**
     * Headless mode via Xvfb (Linux only).
     * - true: start Xvfb virtual display
     * - false/undefined: do not start Xvfb
     * Can also be set via MIDSCENE_COMPUTER_HEADLESS_LINUX=true environment variable.
     */
    headless?: boolean;
    /**
     * Resolution for Xvfb virtual display (default '1920x1080x24')
     */
    xvfbResolution?: string;
    /**
     * Keep a managed Xvfb server alive until process exit.
     *
     * @internal The foreground CLI uses this because libnut keeps a process-wide
     * X11 connection open. Stopping Xvfb during normal CLI teardown would make
     * Xlib terminate an otherwise successful command with exit code 1.
     */
    keepXvfbAliveUntilProcessExit?: boolean;
}

/**
 * Discriminated union describing the two ways `computer_*` tools can spawn an
 * agent. `mode` is filled in by `initArgSpec.adapt` based on whether `host` is
 * set, so callers (CLI/YAML) never have to provide it explicitly.
 */
declare type ComputerInitArgs = ComputerLocalInitArgs | ComputerRDPInitArgs;

export declare type ComputerInterface = ComputerDevice | RDPDevice;

/** Init args for the local desktop agent (macOS/Windows/Linux). */
declare type ComputerLocalInitArgs = {
    mode: 'local';
} & Pick<ComputerDeviceOpt, 'displayId' | 'headless'> & Pick<ComputerDeviceOpt, 'inputStrategy' | 'keyboardTypeDelay'> & AgentBehaviorInitArgs;

/**
 * Computer-specific tools manager
 * Extends BaseMidsceneTools to provide desktop automation tools
 */
export declare class ComputerMidsceneTools extends BaseMidsceneTools<ComputerAgent, ComputerInitArgs> {
    private readonly options;
    private lastInitArgsSignature?;
    constructor(options?: ComputerMidsceneToolsOptions);
    protected getCliReportSessionName(): string;
    protected readonly initArgSpec: InitArgSpec<ComputerInitArgs>;
    protected createTemporaryDevice(): ComputerDevice;
    protected ensureAgent(opts?: ComputerInitArgs): Promise<ComputerAgent>;
    /**
     * Provide Computer-specific platform tools
     */
    protected preparePlatformTools(): ToolDefinition[];
}

declare interface ComputerMidsceneToolsOptions {
    /** Keep CLI-owned Xvfb alive until process exit so Xlib clients stay valid. */
    keepXvfbAliveUntilProcessExit?: boolean;
}

/** Init args for the RDP remote-desktop agent. */
declare type ComputerRDPInitArgs = {
    mode: 'rdp';
} & RDPConnectionConfig & Pick<ComputerDeviceOpt, 'inputStrategy' | 'keyboardTypeDelay'> & AgentBehaviorInitArgs;

export declare function createDefaultRDPBackendClient(): RDPBackendClient;

export declare interface DisplayInfo {
    id: string;
    name: string;
    primary?: boolean;
}

declare interface EnvironmentCheck {
    available: boolean;
    error?: string;
    platform: string;
    displays: number;
}

/**
 * Get all connected displays
 */
export declare function getConnectedDisplays(): Promise<DisplayInfo[]>;

export declare class HelperProcessRDPBackendClient implements RDPBackendClient {
    private readonly spawnFn;
    private readonly resolveHelperPath;
    private child?;
    private stdoutReader?;
    private readonly pending;
    private readonly helperDiagnostics;
    private nextRequestId;
    private connected;
    private fatalHelperError?;
    constructor(options?: {
        spawnFn?: SpawnFn;
        helperPath?: string;
    });
    connect(config: RDPConnectionConfig): Promise<RDPConnectionInfo>;
    disconnect(): Promise<void>;
    screenshotBase64(): Promise<string>;
    size(): Promise<Size>;
    mouseMove(x: number, y: number): Promise<void>;
    mouseButton(button: RDPMouseButton, action: RDPMouseButtonAction): Promise<void>;
    wheel(direction: RDPScrollDirection, amount: number, x?: number, y?: number): Promise<void>;
    keyPress(keyName: string): Promise<void>;
    typeText(text: string): Promise<void>;
    clearInput(): Promise<void>;
    private ensureHelperStarted;
    private handleHelperStreamError;
    private handleStdoutLine;
    private captureStderrChunk;
    private send;
    private expectOk;
    private rejectPending;
    private rejectPendingForChild;
    private createHelperError;
    private disposeReaders;
    private shutdownHelper;
}

export declare type LocalComputerAgentOpt = BaseComputerAgentOpt & Omit<ComputerDeviceOpt, keyof ComputerAgentSharedDeviceOpt>;

/**
 * Determine whether Xvfb is needed.
 * Only starts when explicitly requested via `headless: true`.
 * Non-Linux platforms always return false.
 */
export declare function needsXvfb(explicitOpt?: boolean): boolean;

export { overrideAIConfig }

export declare interface RDPBackendClient {
    connect(config: RDPConnectionConfig): Promise<RDPConnectionInfo>;
    disconnect(): Promise<void>;
    screenshotBase64(): Promise<string>;
    size(): Promise<Size>;
    mouseMove(x: number, y: number): Promise<void>;
    mouseButton(button: RDPMouseButton, action: RDPMouseButtonAction): Promise<void>;
    wheel(direction: RDPScrollDirection, amount: number, x?: number, y?: number): Promise<void>;
    keyPress(keyName: string): Promise<void>;
    typeText(text: string): Promise<void>;
    clearInput?(): Promise<void>;
}

export declare type RDPComputerAgentOpt = BaseComputerAgentOpt & Omit<RDPDeviceOpt, keyof ComputerAgentSharedDeviceOpt>;

export declare interface RDPConnectionConfig {
    host: string;
    port?: number;
    username?: string;
    password?: string;
    domain?: string;
    localAddress?: string;
    adminSession?: boolean;
    ignoreCertificate?: boolean;
    securityProtocol?: RDPSecurityProtocol;
    desktopWidth?: number;
    desktopHeight?: number;
}

export declare interface RDPConnectionInfo {
    sessionId: string;
    size: Size;
    server: string;
}

export declare class RDPDevice implements AbstractInterface {
    interfaceType: InterfaceType;
    private readonly connectionConfig;
    private readonly inputOptions;
    private readonly customActions;
    private readonly backend;
    private connectionInfo?;
    private destroyed;
    private cursorPosition?;
    uri?: string;
    readonly inputPrimitives: ComputerInputPrimitives;
    private typeText;
    constructor(options: RDPDeviceOpt);
    describe(): string;
    connect(): Promise<void>;
    screenshotBase64(): Promise<string>;
    size(): Promise<Size>;
    destroy(): Promise<void>;
    actionSpace(): DeviceAction<any>[];
    private assertConnected;
    private throwIfDestroyed;
    private moveToElement;
    private clearInput;
    private edgeScrollDirection;
    private defaultScrollDistance;
    private movePointer;
    private performWheel;
}

export declare interface RDPDeviceOpt extends RDPConnectionConfig, ComputerDeviceInputOpt {
    backend?: RDPBackendClient;
    customActions?: DeviceAction<any>[];
}

export declare interface RDPHelperEnvelope<TPayload> {
    id: string;
    payload: TPayload;
}

export declare type RDPHelperRequest = RDPHelperEnvelope<RDPProtocolRequest>;

export declare type RDPHelperResponse = {
    id: string;
    ok: true;
    payload: RDPProtocolResponse;
} | {
    id: string;
    ok: false;
    error: {
        message: string;
        code?: string;
    };
};

export declare type RDPMouseButton = 'left' | 'right' | 'middle';

export declare type RDPMouseButtonAction = 'down' | 'up' | 'click' | 'doubleClick';

export declare type RDPProtocolRequest = {
    type: 'connect';
    config: RDPConnectionConfig;
} | {
    type: 'disconnect';
} | {
    type: 'screenshot';
} | {
    type: 'size';
} | {
    type: 'mouseMove';
    x: number;
    y: number;
} | {
    type: 'mouseButton';
    button: RDPMouseButton;
    action: RDPMouseButtonAction;
} | {
    type: 'wheel';
    direction: RDPScrollDirection;
    amount: number;
    x?: number;
    y?: number;
} | {
    type: 'keyPress';
    keyName: string;
} | {
    type: 'typeText';
    text: string;
} | {
    type: 'clearInput';
};

export declare type RDPProtocolResponse = {
    type: 'ok';
} | {
    type: 'connected';
    info: RDPConnectionInfo;
} | {
    type: 'size';
    size: Size;
} | {
    type: 'screenshot';
    base64: string;
};

export declare type RDPScrollDirection = 'up' | 'down' | 'left' | 'right';

export declare type RDPSecurityProtocol = 'auto' | 'tls' | 'nla' | 'rdp';

declare type SpawnFn = (command: string, args?: readonly string[], options?: SpawnOptionsWithoutStdio) => ChildProcessWithoutNullStreams;

export declare class UnsupportedRDPBackendClient implements RDPBackendClient {
    connect(_config: RDPConnectionConfig): Promise<RDPConnectionInfo>;
    disconnect(): Promise<void>;
    screenshotBase64(): Promise<string>;
    size(): Promise<Size>;
    mouseMove(_x: number, _y: number): Promise<void>;
    mouseButton(_button: RDPMouseButton, _action: RDPMouseButtonAction): Promise<void>;
    wheel(_direction: RDPScrollDirection, _amount: number, _x?: number, _y?: number): Promise<void>;
    keyPress(_keyName: string): Promise<void>;
    typeText(_text: string): Promise<void>;
    clearInput(): Promise<void>;
}

export declare function version(): string;

export { }
