/**
 * CauseWise inspector panel for core/button and core/buttons.
 * Loaded only in the block editor. It does not call the tracker.
 */
import {
  attributesAfterToggle,
  extendBlockSettings,
  MAX_PROPERTIES,
  normalizeEventNameInput,
  normalizePropertyKeyInput,
  normalizePropertyValueInput,
  normalizeStoredRows,
  rowsForAttribute,
  safeSettingsUrl,
  sanitizeEventName,
  supportsBlock,
  trackingExplanation,
  trackingSummary,
  type BlockTrackingAttributes,
  type PropertyRow,
} from "./block-tracking";

type CreateElement = (
  type: unknown,
  props?: Record<string, unknown> | null,
  ...children: unknown[]
) => unknown;

type WpNamespace = {
  hooks?: { addFilter: (hook: string, name: string, callback: (...args: never[]) => unknown) => void };
  element?: {
    createElement?: CreateElement;
    Fragment?: unknown;
    useState?: <T>(initial: T | (() => T)) => [T, (value: T | ((current: T) => T)) => void];
    useEffect?: (effect: () => void | (() => void), deps?: unknown[]) => void;
  };
  components?: {
    PanelBody?: unknown;
    ToggleControl?: unknown;
    TextControl?: unknown;
    Button?: unknown;
  };
  blockEditor?: { InspectorControls?: unknown };
  compose?: {
    createHigherOrderComponent?: (
      factory: (BlockEdit: unknown) => (props: BlockEditProps) => unknown,
      name: string,
    ) => (BlockEdit: unknown) => (props: BlockEditProps) => unknown;
  };
  i18n?: { __?: (text: string, domain?: string) => string };
};

type BlockEditProps = {
  name?: string;
  clientId?: string;
  attributes?: BlockTrackingAttributes;
  setAttributes?: (next: Partial<BlockTrackingAttributes>) => void;
  [key: string]: unknown;
};

export type EditorConfig = {
  trackingEnabled: boolean;
  settingsUrl: string;
};

const DOMAIN = "causewise-analytics";

function copyRows(input: unknown): PropertyRow[] {
  return normalizeStoredRows(input).map((row) => ({ key: row.key, value: row.value }));
}

export function registerCauseWiseBlockPanel(api: WpNamespace, config: EditorConfig): boolean {
  const createElement = api.element?.createElement;
  const Fragment = api.element?.Fragment;
  const useState = api.element?.useState;
  const useEffect = api.element?.useEffect;
  const PanelBody = api.components?.PanelBody;
  const ToggleControl = api.components?.ToggleControl;
  const TextControl = api.components?.TextControl;
  const Button = api.components?.Button;
  const InspectorControls = api.blockEditor?.InspectorControls;
  const addFilter = api.hooks?.addFilter;
  const hoc = api.compose?.createHigherOrderComponent;
  if (
    !createElement ||
    !Fragment ||
    !useState ||
    !useEffect ||
    !PanelBody ||
    !ToggleControl ||
    !TextControl ||
    !Button ||
    !InspectorControls ||
    !addFilter ||
    !hoc
  ) {
    return false;
  }

  const __ = api.i18n?.__ ?? ((text: string) => text);
  const trackingEnabled = !!config.trackingEnabled;
  const settingsUrl = safeSettingsUrl(config.settingsUrl);

  function CauseWisePanel(props: BlockEditProps) {
    const attributes = props.attributes ?? {};
    const attributeEncoded = JSON.stringify(copyRows(attributes.causewiseProps));
    const rowState = useState<PropertyRow[]>(() => copyRows(attributes.causewiseProps));
    const rows = rowState[0];
    const setRows = rowState[1];
    const messageState = useState("");
    const message = messageState[0];
    const setMessage = messageState[1];

    const cleanedState = useState(false);
    const cleaned = cleanedState[0];
    const setCleaned = cleanedState[1];
    useEffect(() => {
      if (cleaned) return;
      setCleaned(true);
      const committed = rowsForAttribute(copyRows(attributes.causewiseProps));
      if (JSON.stringify(committed.rows) !== attributeEncoded) {
        props.setAttributes?.({ causewiseProps: committed.rows });
        setRows(committed.rows);
      }
    }, [attributeEncoded]);

    function commitRows(next: PropertyRow[]) {
      const committed = rowsForAttribute(next);
      setRows(next.map((row) => ({ key: row.key, value: row.value })));
      setMessage(committed.message);
      const current = JSON.stringify(copyRows(attributes.causewiseProps));
      if (JSON.stringify(committed.rows) !== current) {
        props.setAttributes?.({ causewiseProps: committed.rows });
      }
    }

    const enabled = !!attributes.causewiseTrack;
    const eventCheck = sanitizeEventName(attributes.causewiseEvent ?? "");
    const explanation = trackingExplanation(props.name || "", attributes, trackingEnabled);
    const summary = trackingSummary(attributes);
    const notices = [];
    if (enabled && !eventCheck.ok && eventCheck.message) notices.push(eventCheck.message);
    if (message) notices.push(message);

    const children: unknown[] = [
      createElement(
        "p",
        { className: "causewise-block-help" },
        explanation,
      ),
    ];

    if (!trackingEnabled && settingsUrl) {
      children.push(
        createElement(
          "p",
          { className: "causewise-block-help" },
          createElement(
            "a",
            { href: settingsUrl },
            __("Open CauseWise settings", DOMAIN),
          ),
        ),
      );
    }

    children.push(
      createElement(ToggleControl, {
        label: __("Enable event tracking", DOMAIN),
        checked: enabled,
        help: __("Sends a click event with the CauseWise tracker already on this site.", DOMAIN),
        onChange: (value: boolean) => {
          const next = attributesAfterToggle(attributes, !!value);
          props.setAttributes?.(next);
          if (next.causewiseProps) setRows(copyRows(next.causewiseProps));
          setMessage("");
        },
      }),
    );

    if (enabled) {
      children.push(
        createElement(TextControl, {
          label: __("Event name", DOMAIN),
          value: attributes.causewiseEvent || "",
          help: eventCheck.ok
            ? __("Use letters, numbers, and underscores, like button_click. Create a CauseWise goal with this same name.", DOMAIN)
            : eventCheck.message,
          onChange: (value: string) => {
            props.setAttributes?.({ causewiseEvent: normalizeEventNameInput(value) });
          },
        }),
      );
      children.push(
        createElement(
          "p",
          { className: "causewise-block-summary" },
          createElement("strong", null, __("What is tracked", DOMAIN)),
        ),
      );
      children.push(createElement("p", { className: "causewise-block-summary" }, summary));
      children.push(
        createElement(
          "p",
          { className: "causewise-block-help" },
          __("Optional event properties. Example: location and hero. Do not enter emails, names, or other personal details.", DOMAIN),
        ),
      );

      rows.forEach((row, index) => {
        children.push(
          createElement(
            "div",
            { className: "causewise-block-prop-row", key: `prop-${index}` },
            createElement(TextControl, {
              label: index === 0 ? __("Property name", DOMAIN) : `${__("Property name", DOMAIN)} ${index + 1}`,
              value: row.key,
              onChange: (value: string) => {
                const next = rows.map((item) => ({ key: item.key, value: item.value }));
                next[index] = { key: normalizePropertyKeyInput(value), value: row.value };
                commitRows(next);
              },
            }),
            createElement(TextControl, {
              label: index === 0 ? __("Property value", DOMAIN) : `${__("Property value", DOMAIN)} ${index + 1}`,
              value: row.value,
              onChange: (value: string) => {
                const next = rows.map((item) => ({ key: item.key, value: item.value }));
                next[index] = { key: row.key, value: normalizePropertyValueInput(value) };
                commitRows(next);
              },
            }),
            createElement(
              Button,
              {
                variant: "link",
                isDestructive: true,
                onClick: () => {
                  commitRows(rows.filter((_, rowIndex) => rowIndex !== index));
                },
              },
              __("Remove property", DOMAIN),
            ),
          ),
        );
      });

      children.push(
        createElement(
          Button,
          {
            variant: "secondary",
            disabled: rows.length >= MAX_PROPERTIES,
            onClick: () => {
              if (rows.length >= MAX_PROPERTIES) return;
              setRows(rows.concat([{ key: "", value: "" }]));
            },
          },
          __("Add property", DOMAIN),
        ),
      );
    }

    if (notices.length > 0) {
      children.push(
        createElement(
          "p",
          { className: "causewise-block-warning", role: "status" },
          notices.join(" "),
        ),
      );
    }

    return createElement(
      PanelBody,
      {
        title: __("CauseWise", DOMAIN),
        initialOpen: true,
      },
      createElement(
        "div",
        { className: "causewise-block-panel" },
        ...children,
      ),
    );
  }

  addFilter(
    "blocks.registerBlockType",
    "causewise-analytics/button-attributes",
    ((settings: { attributes?: Record<string, unknown> }, name: string) =>
      extendBlockSettings(settings, name)) as (...args: never[]) => unknown,
  );

  const withPanel = hoc((BlockEdit: unknown) => {
    return function CauseWiseBlockEdit(props: BlockEditProps) {
      if (!supportsBlock(props.name || "")) return createElement(BlockEdit as never, props);
      return createElement(
        Fragment,
        null,
        createElement(BlockEdit as never, props),
        createElement(
          InspectorControls,
          null,
          createElement(CauseWisePanel as never, {
            key: props.clientId || props.name,
            name: props.name,
            clientId: props.clientId,
            attributes: props.attributes,
            setAttributes: props.setAttributes,
          }),
        ),
      );
    };
  }, "withCauseWiseButtonTracking");

  addFilter(
    "editor.BlockEdit",
    "causewise-analytics/button-panel",
    withPanel as (...args: never[]) => unknown,
  );
  return true;
}

const editorApi = (globalThis as { wp?: WpNamespace }).wp;
const editorConfig = (globalThis as { causewiseBlockEditor?: EditorConfig }).causewiseBlockEditor;

if (editorApi) {
  registerCauseWiseBlockPanel(editorApi, {
    trackingEnabled: !!editorConfig?.trackingEnabled,
    settingsUrl: typeof editorConfig?.settingsUrl === "string" ? editorConfig.settingsUrl : "",
  });
}
