// Hands-On tutorial — RELATED-LIST Item Action.
//
// Per-row button rendered in the "Hands-On Assets" related-list's
// row-action column on the movie edit page. The shell renders one
// instance per row that passes its permission gate:
//
//   Object.entries(itemActions)
//     .filter(([key]) => row.permissions[key])
//     .map(([, component]) => component);
//
// The federation KEY (`edit` in main.tsx) is looked up against
// `row.permissions[key]`. By piggybacking on `edit` — a permission
// the BFF already emits per row — we get "show this action on
// every row the user can edit" for free, without inventing a new
// backend permission key or hand-wiring page-component-config
// rows. The trade-off: the federation key is also the visibility
// flag, so two different itemActions can't both gate on `edit`
// without merging.
//
// Props delivered by the core ItemActions renderer:
//   - row     — the row data (id, fields, permissions)
//   - items   — every visible row on the current page
//   - events  — refresh / delete / etc. on the host related-list

import React, { useState } from "react";
import { TextButton, Tooltip, successMessage, errorMessage } from "@agilecontent/ui";
import { ExperimentOutlined } from "@agilecontent/ui/icons";
import { type Decorators } from "@agilecontent/mib-modules";
import type { ListTypes } from "@agilecontent/mib-api-connector";
import { publishAssetEvent } from "../hooks/useAssetEvents";

type Row = Omit<ListTypes.ListComponentItem, "id"> & { id: string | number };

type Props = {
  row: Row;
  items: Row[];
  events?: Decorators["relatedList"]["events"];
};

export default function ProbeAssetAction({ row, events }: Props) {
  const [busy, setBusy] = useState(false);

  const onClick = async (e: React.MouseEvent) => {
    // preventDefault + stopPropagation kills the default form-submit
    // path (the action sits inside the host edit-page's <form>) and
    // any synthetic bubble that would surface the click to the
    // form's submit handler. Combined with `htmlType="button"` below
    // this is enough — the current shell's related-list does NOT
    // attach a row-level onClick to the <tr>, so there's no native
    // bubble to fight.
    e.preventDefault();
    e.stopPropagation();
    setBusy(true);
    try {
      // Demo stub: real-world code would POST to a backend endpoint
      // here, e.g.
      //
      //   const { data } = await (window as any).cms.instance.post(
      //     `/api/v2/handson/assets/${row.id}/probe`,
      //   );
      //
      // `window.cms.instance` is the shell's pre-configured axios
      // (its request interceptor attaches `Authorization: Bearer
      // <jwt>` automatically — bare `fetch` would land
      // unauthenticated and 302-redirect to /login). The bundled
      // `AssetsController` in backend/Controllers/AssetsController.cs
      // shows the C# side. NOTE: that controller MUST inherit from
      // `ApiBaseController` (the BFF's `Controllers/ApiBaseController.cs`)
      // to ride the existing `[Authorize(AuthenticationSchemes =
      // Constants.API_SCHEME)]` wiring; a plain `ControllerBase`
      // subclass triggers `WorkflowFactoryMiddleware` to throw 401
      // before the action body runs, and the shell's axios 401
      // interceptor then redirects to /login. The hands-on
      // distribution keeps the controller as plain `ControllerBase`
      // so the tutorial doesn't pull `ApiBaseController` into the
      // build dependencies — and the action below fakes the round-
      // trip on the client so the rest of the wiring (toast,
      // events.refresh, event-bus pulse on the dashboard) is still
      // observable end-to-end.
      await new Promise((r) => setTimeout(r, 250)); // simulate network
      const idNum =
        typeof row.id === "number" ? row.id : parseInt(String(row.id), 10) || 0;
      const duration = 60 + (idNum % 240);              // deterministic stub
      const size     = 256_000 + idNum * 17_000;        // deterministic stub
      successMessage(
        `Probed asset #${row.id}: duration ${duration}s, ${Math.round(size / 1024)} KB`,
      );
      // Tell the host RelatedList to re-fetch — the probe may have
      // updated SIZE_BYTES on the row.
      events?.refresh?.();
      // Broadcast to any listener (the dashboard widget at the top
      // of the page re-aggregates its counts).
      publishAssetEvent("probed", { id: row.id, duration, size });
    } catch (e: any) {
      errorMessage(`Probe failed: ${e.message}`);
    } finally {
      setBusy(false);
    }
  };

  return (
    <Tooltip title="Probe metadata">
      <TextButton
        // Explicit `type="button"` is mandatory — the action lives
        // inside the host edit-page's <form>. Without it the
        // underlying antd <button> defaults to `type="submit"` and
        // the click POSTs the entire movie record to the page URL
        // instead of running our action.
        htmlType="button"
        loading={busy}
        icon={<ExperimentOutlined />}
        onClick={onClick}
      />
    </Tooltip>
  );
}
