import { RATINGS, type Rating } from "@/lib/order-reviews";

/**
 * The order nudge as Block Kit, so the answer happens IN Slack.
 *
 * A link to the app was the first version and it was the wrong shape: being
 * sent to a web app to answer "was this shared?" is enough friction that the
 * question stops getting answered, which is the whole failure the nudge exists
 * to prevent (user, 2026-07-28).
 *
 * Buttons carry `<transactionId>:<verb>` in `value`. The transaction id has to
 * travel in the payload because Slack gives the handler nothing else to
 * identify the row — the message text is not a key.
 */

export interface NudgeState {
  transactionId: number;
  merchant: string;
  currency: string;
  total: number;
  isFamily?: boolean;
  shared: boolean;
  /** Ratings already recorded, as participant name → rating. */
  ratings?: { name: string; rating: Rating }[];
  warn?: boolean;
  warnNote?: string | null;
}

const RATING_LABEL: Record<Rating, string> = {
  loved: "Loved it",
  liked: "Liked it",
  ok: "OK",
  bad: "Bad",
  never: "Never again",
};

export function nudgeBlocks(s: NudgeState) {
  const lines = [
    `:receipt: *${s.merchant}* — ${s.currency} ${s.total.toFixed(2)}` +
      (s.isFamily ? "  ·  [Family]" : ""),
  ];
  if (s.warn) {
    lines.push(
      `:warning: You marked this merchant *never again* before` +
        (s.warnNote ? ` — _${s.warnNote}_` : "")
    );
  }
  if (s.ratings?.length) {
    lines.push(s.ratings.map((r) => `${r.name}: *${RATING_LABEL[r.rating]}*`).join("   ·   "));
  }

  return [
    { type: "section", text: { type: "mrkdwn", text: lines.join("\n") } },
    // A select, not four buttons. Slack's mobile client gives every button in
    // an actions block its own full-width row, so four ratings became four
    // stacked bars and the card filled the screen. One select is one row and
    // still one decision; the extra tap to open it is the price of a nudge you
    // can read at a glance.
    {
      type: "actions",
      block_id: "rate",
      elements: [
        {
          type: "static_select",
          action_id: "rate",
          placeholder: { type: "plain_text", text: "How was it?" },
          options: RATINGS.map((r) => ({
            text: { type: "plain_text", text: RATING_LABEL[r] },
            value: `${s.transactionId}:rate:${r}`,
          })),
        },
      ],
    },
    {
      type: "actions",
      block_id: "details",
      elements: [
        {
          type: "button",
          action_id: "open_details",
          text: { type: "plain_text", text: "Add details" },
          value: `${s.transactionId}:details`,
        },
      ],
    },
    // Sharing comes LAST. You judge the food, then decide who pays for it —
    // asking "was this shared?" before "was it any good?" inverts the order a
    // person actually thinks in (user, 2026-07-28). The controls are
    // independent and each writes immediately, so this is presentation only.
    {
      type: "actions",
      block_id: "share",
      elements: [
        {
          type: "button",
          action_id: "share_toggle",
          // The label states what pressing it will DO, not the current state.
          // A button labelled with its own state reads as already-pressed and
          // gets tapped to "fix" it, toggling the thing it was reporting.
          text: {
            type: "plain_text",
            text: s.shared ? "Make it just me" : "Shared 50/50",
          },
          style: s.shared ? undefined : "primary",
          value: `${s.transactionId}:share`,
        },
      ],
    },
    {
      type: "context",
      elements: [
        {
          type: "mrkdwn",
          text: s.shared
            ? ":busts_in_silhouette: Split 50/50 — both verdicts welcome"
            : ":bust_in_silhouette: Not shared",
        },
      ],
    },
  ];
}

/**
 * The per-item verdicts and the free-text note, as a Slack modal.
 *
 * A message cannot collect free text and an actions block caps at 25 elements,
 * so this is the only Slack-native way to ask "which dish, and why". It stays
 * inside Slack — no browser, no app — which is the entire reason it exists
 * rather than a link.
 *
 * The overall rating deliberately stays on the card: it is the thing you do
 * every time and it should cost one tap. This is for the times something was
 * notably good or bad.
 *
 * `private_metadata` carries the ids because a view_submission arrives as a
 * fresh request with no reference to the message it came from.
 */
export function detailsModal(
  transactionId: number,
  participantId: number,
  merchant: string,
  items: string[],
  existing: { note?: string | null; itemVerdicts?: { item: string; verdict: string }[] }
) {
  const verdictOf = (item: string) =>
    existing.itemVerdicts?.find(
      (v) => v.item.trim().toLowerCase() === item.trim().toLowerCase()
    )?.verdict ?? null;

  const opt = (text: string, value: string) => ({
    text: { type: "plain_text", text },
    value,
  });

  const itemBlocks = items
    // Slack allows 100 blocks per view; a grocery order can be long, and past
    // ~20 rows nobody is scrolling a modal to rate a tin of tomatoes anyway.
    .slice(0, 20)
    .map((item, i) => {
      const current = verdictOf(item);
      const options = [opt("👍 Great", "loved"), opt("👎 Never again", "never")];
      return {
        type: "input",
        block_id: `item_${i}`,
        optional: true,
        // The label carries the item name; Slack truncates at 150 chars.
        label: { type: "plain_text", text: item.slice(0, 150) },
        element: {
          type: "radio_buttons",
          action_id: "verdict",
          options,
          ...(current
            ? { initial_option: options.find((o) => o.value === current) }
            : {}),
        },
      };
    });

  return {
    type: "modal",
    callback_id: "order_details",
    private_metadata: JSON.stringify({
      t: transactionId,
      p: participantId,
      // The item text is not recoverable from the submission — Slack returns
      // block ids and values, not labels — so it travels with the view.
      i: items.slice(0, 20),
    }),
    title: { type: "plain_text", text: "Order details" },
    submit: { type: "plain_text", text: "Save" },
    close: { type: "plain_text", text: "Cancel" },
    blocks: [
      { type: "section", text: { type: "mrkdwn", text: `*${merchant}*` } },
      {
        type: "input",
        block_id: "note",
        optional: true,
        label: { type: "plain_text", text: "Anything worth remembering?" },
        element: {
          type: "plain_text_input",
          action_id: "value",
          multiline: true,
          initial_value: existing.note ?? undefined,
          placeholder: {
            type: "plain_text",
            text: "e.g. the biryani was decent, sides were cold",
          },
        },
      },
      ...(itemBlocks.length
        ? [{ type: "divider" }, ...itemBlocks]
        : [
            {
              type: "context",
              elements: [
                {
                  type: "mrkdwn",
                  // Uber itemises groceries but not restaurant orders, so an
                  // empty list is the receipt, not a failure.
                  text: "_This receipt has no itemised list._",
                },
              ],
            },
          ]),
    ],
  };
}

/**
 * The card DMed to the other person when an order is shared with them.
 *
 * Sharing splits the money; this is the other half of the same requirement —
 * "if shared then get feedback from other user as well" (user, 2026-07-28).
 * Without it that half was aspirational: she is not in #smarthome, so the
 * channel card she was invited to answer was one she could not see.
 *
 * A DM rather than adding her to the channel, so her surface stays "orders that
 * concern me" instead of the whole house's ops feed.
 *
 * No share button here on purpose. She is being told it was shared, not asked
 * to decide — and a second person toggling the split from a stale copy of the
 * card is a race with no upside.
 */
export function partnerNudgeBlocks(s: NudgeState, sharerName: string) {
  const lines = [
    `:receipt: *${s.merchant}* — ${s.currency} ${s.total.toFixed(2)}`,
    `${sharerName} shared this with you, 50/50. How was it?`,
  ];
  if (s.warn) {
    lines.push(
      `:warning: This merchant was marked *never again* before` +
        (s.warnNote ? ` — _${s.warnNote}_` : "")
    );
  }

  return [
    { type: "section", text: { type: "mrkdwn", text: lines.join("\n") } },
    {
      type: "actions",
      block_id: "rate",
      elements: [
        {
          type: "static_select",
          action_id: "rate",
          placeholder: { type: "plain_text", text: "How was it?" },
          options: RATINGS.map((r) => ({
            text: { type: "plain_text", text: RATING_LABEL[r] },
            value: `${s.transactionId}:rate:${r}`,
          })),
        },
      ],
    },
    {
      type: "actions",
      block_id: "details",
      elements: [
        {
          type: "button",
          action_id: "open_details",
          text: { type: "plain_text", text: "Add details" },
          value: `${s.transactionId}:details`,
        },
      ],
    },
  ];
}
