import { z } from "zod";
import { FAILURE_CODES, FailureError, type ToolDefinition } from "@argent/registry";
import {
  nativeDevtoolsRef,
  precheckNativeDevtools,
  type NativeDevtoolsApi,
  type NativeDevtoolsInitFailedResult,
} from "../../blueprints/native-devtools";
import { resolveDevice } from "../../utils/device-info";
import { ensureDeps } from "../../utils/check-deps";

const zodSchema = z.object({
  udid: z.string().describe("Simulator UDID"),
  bundleId: z.string().describe("Bundle ID of the app"),
  x: z
    .number()
    .describe(
      "Raw X coordinate in the app window's native point space. NOT normalized [0,1] tap space."
    ),
  y: z
    .number()
    .describe(
      "Raw Y coordinate in the app window's native point space. NOT normalized [0,1] tap space."
    ),
  includeAncestors: z
    .boolean()
    .optional()
    .describe("Include ancestor chain for the matched view (default true)"),
  includeChildren: z
    .boolean()
    .optional()
    .describe("Include child views for the matched view (default false)"),
  fields: z
    .array(z.string())
    .optional()
    .describe(
      "View fields to include. Defaults: pointer, className, tag, frame, " +
        "windowFrame, bounds, hidden, alpha, opaque, clipsToBounds, " +
        "userInteractionEnabled, depth, identifier, label, layerName, nativeID. " +
        "Additional: center, transform, contentMode, backgroundColor, tintColor"
    ),
  skipClasses: z
    .array(z.string())
    .optional()
    .describe("Exact UIView class names whose entire subtree should be pruned"),
  skipClassPrefixes: z
    .array(z.string())
    .optional()
    .describe("Class name prefixes to prune entire subtrees"),
  maxDepth: z
    .number()
    .int()
    .positive()
    .optional()
    .describe("Maximum depth for returned child/ancestor serialization (default 150)"),
});

type Params = z.infer<typeof zodSchema>;
type Result =
  | NativeDevtoolsInitFailedResult
  | { status: "restart_required"; message: string }
  | { status: "ok"; view: unknown | null };

export const nativeUserInteractableViewAtPointTool: ToolDefinition<Params, Result> = {
  id: "native-user-interactable-view-at-point",
  interaction: {
    startedMsg: ({ params }) => `Finding interactive view at (${params.x}, ${params.y})`,
    completedMsg: ({ params }) => `Found interactive view at (${params.x}, ${params.y})`,
    failedMsg: ({ params, failureSignal }) =>
      `Failed to find interactive view at (${params.x}, ${params.y}): ${failureSignal.error_code}`,
  },
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
  description: `Inspect the deepest UIView at a raw native window point that would actually receive touch input.

Unlike native-view-at-point, this respects userInteractionEnabled and is closer to
UIKit hit-testing semantics.

IMPORTANT: x and y are raw iOS window coordinates in points, NOT normalized [0,1]
simulator tap coordinates.

If status is restart_required: call restart-app then retry.`,
  zodSchema,
  services: (params) => ({
    nativeDevtools: nativeDevtoolsRef(resolveDevice(params.udid)),
  }),
  async execute(services, params) {
    const device = resolveDevice(params.udid);
    await ensureDeps(device.platform === "ios-remote" ? ["sim-remote"] : ["xcrun"]);

    const api = services.nativeDevtools as NativeDevtoolsApi;

    const blocked = await precheckNativeDevtools(api, params.udid, params.bundleId);
    if (blocked) return blocked;

    const rpcParams: Record<string, unknown> = {
      x: params.x,
      y: params.y,
    };
    if (params.includeAncestors !== undefined) rpcParams.includeAncestors = params.includeAncestors;
    if (params.includeChildren !== undefined) rpcParams.includeChildren = params.includeChildren;
    if (params.fields !== undefined) rpcParams.fields = params.fields;
    if (params.skipClasses !== undefined) rpcParams.skipClasses = params.skipClasses;
    if (params.skipClassPrefixes !== undefined)
      rpcParams.skipClassPrefixes = params.skipClassPrefixes;
    if (params.maxDepth !== undefined) rpcParams.maxDepth = params.maxDepth;

    const result = (await api.queryViewHierarchy(
      params.bundleId,
      "ViewHierarchy.userInteractableViewAtPoint",
      rpcParams
    )) as { view?: unknown | null; error?: string };

    if (result.error) {
      throw new FailureError(result.error, {
        error_code: FAILURE_CODES.NATIVE_DEVTOOLS_USER_INTERACTABLE_VIEW_AT_POINT_ERROR,
        failure_stage: "native_devtools_user_interactable_view_at_point",
        failure_area: "tool_server",
        error_kind: "unknown",
      });
    }

    return { status: "ok", view: result.view ?? null };
  },
};
