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"),
  className: z.string().optional().describe("UIView class name to match (exact, e.g. UIButton)"),
  identifier: z.string().optional().describe("Accessibility identifier to match (exact)"),
  label: z.string().optional().describe("Accessibility label to match (exact)"),
  tag: z.number().int().optional().describe("UIView tag integer to match"),
  nativeID: z.string().optional().describe("React Native nativeID prop to match (exact)"),
  includeAncestors: z
    .boolean()
    .optional()
    .describe("Include ancestor chain for each matched view (default true)"),
  includeChildren: z
    .boolean()
    .optional()
    .describe("Include child views for each matched view (default true)"),
  fields: z
    .array(z.string())
    .optional()
    .describe(
      "View fields to include. Defaults: className, frame, hidden, alpha, " +
        "identifier, label, nativeID, userInteractionEnabled, depth. " +
        "Additional: pointer, tag, windowFrame, bounds, center, opaque, " +
        "clipsToBounds, transform, contentMode, backgroundColor, tintColor, layerName"
    ),
});

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

export const nativeFindViewsTool: ToolDefinition<Params, Result> = {
  id: "native-find-views",
  interaction: {
    startedMsg: ({ params }) => `Searching native views in ${params.bundleId}`,
    completedMsg: ({ params, result }) => {
      const count = result.status === "ok" ? result.matches.length : 0;
      return `Found ${count} native ${count === 1 ? "view" : "views"} in ${params.bundleId}`;
    },
    failedMsg: ({ params, failureSignal }) =>
      `Failed to search native views in ${params.bundleId}: ${failureSignal.error_code}`,
  },
  capability: { apple: { simulator: true, device: true }, appleRemote: { simulator: true } },
  description: `Search for specific UIViews in the running app by class name, accessibility identifier, label, tag, or React Native nativeID.
Use when you need to locate a specific view by its properties without dumping the entire hierarchy.
Returns { status: "ok", matches } with matching views including their frames, properties, optional ancestors, and optional children. Much more targeted than native-full-hierarchy.
At least one of className, identifier, label, tag, or nativeID must be provided.
Fails if native devtools are not connected, the app is not running, or 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> = {};
    if (params.className !== undefined) rpcParams.className = params.className;
    if (params.identifier !== undefined) rpcParams.identifier = params.identifier;
    if (params.label !== undefined) rpcParams.label = params.label;
    if (params.tag !== undefined) rpcParams.tag = params.tag;
    if (params.nativeID !== undefined) rpcParams.nativeID = params.nativeID;
    if (params.includeAncestors !== undefined) rpcParams.includeAncestors = params.includeAncestors;
    if (params.includeChildren !== undefined) rpcParams.includeChildren = params.includeChildren;
    if (params.fields !== undefined) rpcParams.fields = params.fields;

    const result = (await api.queryViewHierarchy(
      params.bundleId,
      "ViewHierarchy.findViews",
      rpcParams
    )) as { matches?: unknown[]; error?: string };

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

    return { status: "ok", matches: result.matches ?? [] };
  },
};
