// Coco Raycast UI adapter contract v1.
// This module is loaded only by the signed declarative worker. It deliberately
// exports protocol components, not DOM elements or Coco's WebView bridge.

import React from "react";

const host = () => {
  if (!globalThis.__cocoRaycastUIHost) {
    throw new Error("Raycast UI host is unavailable outside a Coco declarative session");
  }
  return globalThis.__cocoRaycastUIHost;
};

const component = (name) => {
  const ProtocolComponent = function CocoRaycastProtocolComponent(props = {}) {
    // Raycast commonly supplies ActionPanel through an `actions` prop. Put that
    // element into the reconciler tree so the signed host config can attach it
    // as the node's dedicated actionPanel, never as opaque JSON props.
    const { actions, metadata, children, ...safeProps } = props;
    const hostChildren = [children, metadata, actions].filter((value) => value != null);
    return React.createElement(name, safeProps, ...hostChildren);
  };
  // `emptyView` is a React element prop, rather than a child in the host
  // topology. Mark the signed facade so host config can project only its
  // declarative fields and never serialize a React object/function.
  Object.defineProperty(ProtocolComponent, "__cocoRaycastProtocolName", { value: name });
  return ProtocolComponent;
};

const actionComponent = (kind) => function CocoRaycastActionComponent(props = {}) {
  return React.createElement("Callback", { ...props, __cocoActionKind: kind });
};

export const List = component("List");
List.Section = component("ListSection");
List.Item = component("ListItem");
List.EmptyView = component("ListEmptyView");

export const Detail = component("Detail");
Detail.Metadata = component("MetadataSection");
Detail.Metadata.Label = component("MetadataValue");
Detail.Metadata.TagList = component("MetadataValue");
Detail.Metadata.Link = component("MetadataValue");

export const Form = component("Form");
Form.Description = component("FormDescription");
Form.TextField = component("TextField");
Form.TextArea = component("TextArea");
Form.PasswordField = component("PasswordField");
Form.Checkbox = component("Checkbox");
Form.Dropdown = component("Dropdown");
Form.Dropdown.Item = component("DropdownItem");
Form.DatePicker = component("DatePicker");

export const ActionPanel = component("ActionPanel");
ActionPanel.Section = component("ActionSection");
export const Action = component("Callback");
Action.CopyToClipboard = component("Clipboard");
Action.Paste = component("Paste");
Action.OpenInBrowser = function CocoRaycastOpenInBrowser(props = {}) {
  const { url, ...rest } = props;
  return React.createElement("Open", { ...rest, target: url });
};
// Both are ordinary opaque action tokens to Swift. The signed reconciler
// interprets their declarative target/callback inside the worker, so native
// code never receives a React element or JavaScript function.
Action.SubmitForm = actionComponent("submitForm");
Action.Push = actionComponent("push");

export const Clipboard = Object.freeze({
  readText: () => host().request("clipboardRead", {}),
  // The trusted tier deliberately exposes only the text portion of Raycast's
  // Clipboard.Content. Files, HTML, images, and pasteboard type metadata are
  // not host capabilities here, but callers using `const { text } = await
  // Clipboard.read()` retain their normal Raycast-compatible value shape.
  read: () => host().request("clipboardRead", {}).then((value) => Object.freeze({
    text: typeof value === "string" ? value : "",
  })),
  copy: (content) => host().request("clipboardWrite", { content }).then(() => undefined),
  paste: (content) => host().request("clipboardWrite", { content, paste: true }).then(() => undefined),
});

// These presentation-only constants are intentionally finite. They preserve
// the source-level Raycast spelling used by accepted commands without giving
// extension code arbitrary icon/file or toast-style capabilities.
const builtinIcon = (name) => Object.freeze({ builtin: name });
export const Icon = Object.freeze({
  Switch: builtinIcon("Switch"),
  Trash: builtinIcon("Trash"),
});
export const Toast = Object.freeze({
  Style: Object.freeze({ Success: "success" }),
});

export const LocalStorage = Object.freeze({
  getItem: (key) => host().request("storageRead", { key }),
  setItem: (key, value) => host().request("storageWrite", { key, value }).then(() => undefined),
  removeItem: (key) => host().request("storageWrite", { key, remove: true }).then(() => undefined),
  clear: () => host().request("storageWrite", { clear: true }).then(() => undefined),
});

export const showToast = (toast) => host().request("toast", toast ?? {});
export const showHUD = (title) => host().request("hud", { title });
export const getPreferenceValues = () => host().preferences();
export const getSelectedText = () => host().selectedText();
export const open = (url) => host().request("openRequest", { url }).then(() => undefined);
export const closeMainWindow = () => host().close();
export const useNavigation = () => host().navigation();

export const __cocoRaycastAdapterVersion = "1";
