import { AbstractInterface } from '@midscene/core/device';
import type { ActionParam } from '@midscene/core';
import type { ActionReturn } from '@midscene/core';
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 { DeviceAction } from '@midscene/core';
import type { ElementInfo } from '@midscene/shared/extractor';
import { InitArgSpec } from '@midscene/shared/agent-tools/base-tools';
import { InterfaceType } from '@midscene/core';
import { IOSDeviceOpt } from '@midscene/core/device';
import { MobileInputPrimitives } from '@midscene/core/device';
import { overrideAIConfig } from '@midscene/shared/env';
import { PlaygroundPlatformDescriptor } from '@midscene/playground';
import { Point } from '@midscene/core';
import { Size } from '@midscene/core';
import type { ToolDefinition } from '@midscene/shared/agent-tools/types';
import { WebDriverClient } from '@midscene/webdriver';
import { z } from '@midscene/core';

declare type ActionArgs<T extends DeviceAction> = [ActionParam<T>] extends [undefined] ? [] : [ActionParam<T>];

export declare function agentFromWebDriverAgent(opts?: IOSAgentOpt & IOSDeviceOpt): Promise<IOSAgent>;

export declare function checkIOSEnvironment(): Promise<{
    available: boolean;
    error?: string;
}>;

declare type DeviceActionIOSAppSwitcher = DeviceAction<undefined, void>;

declare type DeviceActionIOSHomeButton = DeviceAction<undefined, void>;

declare type DeviceActionRunWdaRequest = DeviceAction<RunWdaRequestParam, RunWdaRequestReturn>;

export declare class IOSAgent extends Agent<IOSDevice> {
    /**
     * Execute WebDriverAgent API request directly
     * Type-safe wrapper around the RunWdaRequest action from actionSpace
     */
    runWdaRequest: WrappedAction<DeviceActionRunWdaRequest>;
    /**
     * Trigger the system home operation on iOS devices
     */
    home: WrappedAction<DeviceActionIOSHomeButton>;
    /**
     * Trigger the system app switcher operation on iOS devices
     */
    appSwitcher: WrappedAction<DeviceActionIOSAppSwitcher>;
    /**
     * User-provided app name to bundle ID mapping
     */
    private appNameMapping;
    constructor(device: IOSDevice, opts?: IOSAgentOpt);
    /**
     * Launch an iOS app or URL
     * @param uri - App name, bundle ID, or URL to launch
     */
    launch(uri: string): Promise<void>;
    /**
     * Terminate (close) an iOS app by bundle ID
     * @param uri - Bundle ID of the app to terminate
     */
    terminate(uri: string): Promise<void>;
    private createActionWrapper;
}

export declare type IOSAgentOpt = AgentOpt & {
    /**
     * Custom mapping of app names to bundle IDs
     * User-provided mappings will take precedence over default mappings
     */
    appNameMapping?: Record<string, string>;
};

export declare class IOSDevice implements AbstractInterface {
    private deviceId;
    private devicePixelRatio;
    private devicePixelRatioInitialized;
    private destroyed;
    private description;
    private customActions?;
    private wdaBackend;
    private wdaManager;
    /** URL of WDA's native MJPEG server for real-time streaming */
    mjpegStreamUrl: string;
    /** Lazily-started consumer of the WDA MJPEG stream, used by UI observation. */
    private mjpegFrameSource;
    /**
     * Continuous frame-source capability for UI observation. Only wired up when
     * the MJPEG frame source is opt-in enabled; otherwise left undefined so
     * observers fall back to sequential screenshots.
     */
    openFrameSource?: AbstractInterface['openFrameSource'];
    private appNameMapping;
    /** Auto-dismissed input eligible for one immediate submit/navigation key. */
    private pendingKeyboardFollowUp;
    interfaceType: InterfaceType;
    uri: string | undefined;
    options?: IOSDeviceOpt;
    readonly inputPrimitives: MobileInputPrimitives;
    private invalidatePendingKeyboardFollowUp;
    private registerPendingKeyboardFollowUp;
    private consumePendingKeyboardFollowUp;
    private tryAutoDismissKeyboard;
    private tapPoint;
    private doubleTapPoint;
    private longPressPoint;
    private swipePoint;
    private clearInputAt;
    actionSpace(): DeviceAction<any>[];
    private performActionScroll;
    constructor(options?: IOSDeviceOpt);
    describe(): string;
    getConnectedDeviceInfo(): Promise<{
        udid: string;
        name: string;
        model: string;
    } | null>;
    connect(): Promise<void>;
    /**
     * Set the app name to bundle ID mapping
     */
    setAppNameMapping(mapping: Record<string, string>): void;
    /**
     * Resolve app name to bundle ID using the mapping.
     * Comparison is case-insensitive and ignores spaces, dashes, and underscores.
     * Keys in appNameMapping are pre-normalized, so we only need to normalize the input.
     *
     * @param appName The app name to resolve.
     */
    private resolveBundleId;
    launch(uri: string): Promise<IOSDevice>;
    /**
     * Terminate (close) an iOS app by bundle ID.
     * Supports app name resolution via setAppNameMapping when provided.
     */
    terminate(bundleId: string): Promise<void>;
    getElementsInfo(): Promise<ElementInfo[]>;
    getElementsNodeTree(): Promise<any>;
    private initializeDevicePixelRatio;
    getScreenSize(): Promise<{
        width: number;
        height: number;
        scale: number;
    }>;
    size(): Promise<Size>;
    screenshotBase64(): Promise<string>;
    /**
     * Continuous frame source backed by WDA's MJPEG server. WDA's
     * `takeScreenshot` is too slow (~250ms) to sample the screen densely, so
     * observers pull the most recent frame from the continuously-running MJPEG
     * stream instead — near-instant per grab. Frames are already JPEG data
     * URLs, so `decode()` is a pass-through.
     *
     * Opt-in: only wired up as the `openFrameSource` capability (in the
     * constructor) when `wdaMjpegFrameSource.enabled` is set, mirroring scrcpy.
     * `stop()` tears the stream down so device-side encoding only runs while an
     * observation window is open.
     */
    private openMjpegFrameSource;
    private ensureMjpegFrameSource;
    clearInput(element?: ElementInfo): Promise<void>;
    url(): Promise<string>;
    tap(x: number, y: number): Promise<void>;
    swipe(fromX: number, fromY: number, toX: number, toY: number, duration?: number): Promise<void>;
    private swipeCoordinates;
    private typeText;
    private pressKey;
    scrollUp(distance?: number, startPoint?: Point): Promise<void>;
    scrollDown(distance?: number, startPoint?: Point): Promise<void>;
    scrollLeft(distance?: number, startPoint?: Point): Promise<void>;
    scrollRight(distance?: number, startPoint?: Point): Promise<void>;
    scrollUntilTop(startPoint?: Point): Promise<void>;
    scrollUntilBottom(startPoint?: Point): Promise<void>;
    private compareScreenshots;
    private scrollUntilBoundary;
    scrollUntilLeft(startPoint?: Point): Promise<void>;
    scrollUntilRight(startPoint?: Point): Promise<void>;
    home(): Promise<void>;
    appSwitcher(): Promise<void>;
    /**
     * Hides the iOS software keyboard using a structurally located accessory
     * control, or configured key names when provided.
     *
     * @returns `true` when the keyboard is hidden, or `false` when the current
     * application exposes no supported dismissal control.
     * @throws When communication with WebDriverAgent fails.
     */
    hideKeyboard(keyNames?: string[]): Promise<boolean>;
    /**
     * Open a URL using WebDriverAgent
     * @param url The URL to open (supports http://, https://, and custom schemes)
     * @param options Configuration options for URL opening
     */
    openUrl(url: string, options?: {
        useSafariAsBackup?: boolean;
        waitTime?: number;
    }): Promise<void>;
    /**
     * Open a URL via Safari (backup method for real devices)
     * @param url The URL to open
     */
    openUrlViaSafari(url: string): Promise<void>;
    /**
     * Execute a WebDriverAgent API request directly
     * This is the iOS equivalent of Android's runAdbShell
     * @param method HTTP method (GET, POST, DELETE, PUT)
     * @param endpoint WebDriver API endpoint
     * @param data Optional request body data
     * @returns Response from the WebDriver API
     */
    runWdaRequest<TResult = any>(method: WDAHttpMethod, endpoint: string, data?: any): Promise<TResult>;
    destroy(): Promise<void>;
}

declare type IOSInitArgs = AgentBehaviorInitArgs & Pick<IOSDeviceOpt, 'wdaHost' | 'wdaPort' | 'sessionId' | 'wdaMjpegPort' | 'wdaMjpegFrameSource'>;

/**
 * iOS-specific tools manager
 * Extends BaseMidsceneTools to provide iOS WebDriverAgent connection tools
 */
export declare class IOSMidsceneTools extends BaseMidsceneTools<IOSAgent, IOSInitArgs> {
    protected getCliReportSessionName(): string;
    protected readonly initArgSpec: InitArgSpec<IOSInitArgs>;
    private lastOptsSignature?;
    protected createTemporaryDevice(): IOSDevice;
    protected ensureAgent(opts?: IOSInitArgs): Promise<IOSAgent>;
    /**
     * Provide iOS-specific platform tools
     */
    protected preparePlatformTools(): ToolDefinition[];
}

declare interface IOSPlatformOptions {
    staticDir?: string;
    getAgentOptions?: () => IOSAgentOpt;
}

export declare const iosPlaygroundPlatform: PlaygroundPlatformDescriptor<IOSPlatformOptions | undefined>;

export declare class IOSWebDriverClient extends WebDriverClient {
    launchApp(bundleId: string): Promise<void>;
    activateApp(bundleId: string): Promise<void>;
    terminateApp(bundleId: string): Promise<void>;
    openUrl(url: string): Promise<void>;
    pressHomeButton(): Promise<void>;
    appSwitcher(): Promise<void>;
    pressKey(key: string): Promise<void>;
    /**
     * Get the currently focused element's WebDriver ID
     * @returns WebDriver element ID or null if no element is focused
     */
    getActiveElement(): Promise<string | null>;
    /**
     * Clear an element using WebDriver's clear endpoint
     * @param elementId WebDriver element ID
     */
    clearElement(elementId: string): Promise<void>;
    /**
     * Clear the currently focused input field using WebDriver Clear API
     * @returns true if successful, false otherwise
     */
    clearActiveElement(): Promise<boolean>;
    private normalizeKeyName;
    private getResponseValue;
    private getElementId;
    private getElementIds;
    private remainingKeyboardDismissTimeout;
    private makeKeyboardDismissRequest;
    private findElementIds;
    private getElementRect;
    private clickElement;
    private findVisibleKeyboardIds;
    private getVisibleKeyboardRect;
    private dismissKeyboardByName;
    private dismissKeyboardUsingAccessoryToolbar;
    private waitForKeyboardToHide;
    /**
     * Hides the visible software keyboard and waits until WDA confirms it is gone.
     *
     * By default this locates a standard accessory toolbar next to the keyboard.
     * It requires left-side navigation controls and exactly one right-edge control
     * before clicking, without depending on localized text. Explicit key names are
     * only matched within the keyboard's nearby region.
     *
     * @returns `true` when the keyboard is hidden, or `false` when no supported
     * dismissal control exists or the keyboard remains visible after the timeout.
     * @throws When WDA returns an invalid response or a request fails.
     */
    dismissKeyboard(keyNames?: string[]): Promise<boolean>;
    /** Returns whether WDA currently exposes a visible software keyboard. */
    isKeyboardVisible(): Promise<boolean>;
    /**
     * Send raw key events without trimming whitespace.
     * Unlike typeText(), this preserves spaces and newlines.
     * Used by the per-character typing delay path where each character
     * must be delivered exactly as-is.
     */
    typeRawKeys(chars: string[]): Promise<void>;
    typeText(text: string): Promise<void>;
    tap(x: number, y: number): Promise<void>;
    swipe(fromX: number, fromY: number, toX: number, toY: number, duration?: number): Promise<void>;
    pinch(centerX: number, centerY: number, startDistance: number, endDistance: number, duration?: number): Promise<void>;
    longPress(x: number, y: number, duration?: number): Promise<void>;
    doubleTap(x: number, y: number): Promise<void>;
    tripleTap(x: number, y: number): Promise<void>;
    getScreenScale(): Promise<number | null>;
    createSession(capabilities?: any): Promise<any>;
    private setupIOSSession;
    setupExistingSession(): Promise<void>;
    /**
     * Execute a WebDriverAgent API request directly
     * This is the iOS equivalent of Android's runAdbShell
     * @param method HTTP method (GET, POST, DELETE, etc.)
     * @param endpoint WebDriver API endpoint
     * @param data Optional request body data
     * @returns Response from the WebDriver API
     */
    executeRequest<TResult = any>(method: string, endpoint: string, data?: any): Promise<TResult>;
}

export { overrideAIConfig }

declare type RunWdaRequestParam = z.infer<typeof runWdaRequestParamSchema>;

declare const runWdaRequestParamSchema: z.ZodObject<{
    method: z.ZodEnum<["GET", "POST", "DELETE", "PUT"]>;
    endpoint: z.ZodString;
    data: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>;
}, "strip", z.ZodTypeAny, {
    method: "POST" | "GET" | "DELETE" | "PUT";
    endpoint: string;
    data?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
}, {
    method: "POST" | "GET" | "DELETE" | "PUT";
    endpoint: string;
    data?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined;
}>;

declare type RunWdaRequestReturn = Awaited<ReturnType<IOSDevice['runWdaRequest']>>;

/**
 * HTTP methods supported by WebDriverAgent API
 */
declare const WDA_HTTP_METHODS: readonly ["GET", "POST", "DELETE", "PUT"];

declare type WDAHttpMethod = (typeof WDA_HTTP_METHODS)[number];

/**
 * Helper type to convert DeviceAction to wrapped method signature
 */
declare type WrappedAction<T extends DeviceAction> = (...args: ActionArgs<T>) => Promise<ActionReturn<T>>;

export { }
