/**
 * Parses Telegram-specific block markup out of an agent's `<external>`
 * payload, so the relay can dispatch rich messages instead of always
 * falling back to plain text.
 *
 * Supported tags (all are OPTIONAL — agents can also just emit text):
 *
 *   <actions>[{"label":"Continue","value":"continue"}, …]</actions>
 *     Renders as an inline-keyboard at the END of the relayed message.
 *     The button `value` is what the user "types" back when they tap.
 *
 *   <image src="https://…" alt="…">caption</image>
 *     Routes to `bot.sendPhoto`. `src` is required (URL or local
 *     file path); `caption` is the inner text; `alt` is fallback.
 *
 *   <attachment src="…" type="…">caption</attachment>
 *     Routes to `bot.sendDocument`. `type` is informational only.
 *
 * Everything OUTSIDE these tags is treated as the text body. Tags are
 * removed from the text body; their semantic info goes into a parsed
 * structure the relay can iterate.
 *
 * Design notes:
 *   - The parser is pure. No bot dependencies. Trivial to unit test.
 *   - Tag matching is lenient: case-insensitive, attribute order
 *     irrelevant, single- or double-quoted attribute values.
 *   - Malformed JSON in <actions> falls through gracefully — the
 *     block is silently dropped rather than crashing the relay.
 *   - Multiple blocks of the same type are allowed and concatenated
 *     in document order.
 */

const ACTIONS_RE    = /<actions(?:\s[^>]*)?>([\s\S]*?)<\/actions>/gi;
const IMAGE_RE      = /<image(\s[^>]*)?>([\s\S]*?)<\/image>/gi;
const ATTACHMENT_RE = /<attachment(\s[^>]*)?>([\s\S]*?)<\/attachment>/gi;

const ATTR_RE = /(\w+)\s*=\s*"([^"]*)"|(\w+)\s*=\s*'([^']*)'/g;

function parseAttributes(attrString) {
  const out = {};
  if (!attrString) return out;
  ATTR_RE.lastIndex = 0;
  let m;
  while ((m = ATTR_RE.exec(attrString)) !== null) {
    const key = m[1] || m[3];
    const val = m[2] !== undefined ? m[2] : m[4];
    if (key) out[key.toLowerCase()] = val;
  }
  return out;
}

/**
 * @typedef {Object} ParsedActionButton
 * @property {string} label   What the user sees on the button.
 * @property {string} value   What the user "types" when tapping.
 *                            Limited to 64 bytes when used as Telegram
 *                            callback_data (we truncate at relay time).
 */

/**
 * @typedef {Object} ParsedMedia
 * @property {'image'|'document'} kind
 * @property {string}             src       URL or local file path.
 * @property {string}             caption   Inner text of the tag.
 * @property {string|null}        alt       Image alt fallback (image only).
 * @property {string|null}        type      MIME-ish hint (document only).
 */

/**
 * @typedef {Object} ParsedTelegramPayload
 * @property {string}             text      Plain message body (tags removed).
 * @property {ParsedActionButton[]} actions Buttons (may be empty).
 * @property {ParsedMedia[]}      media     Media items (may be empty).
 */

/**
 * Parse an `<external>` block body into Telegram-ready pieces.
 *
 * @param {string} body
 * @returns {ParsedTelegramPayload}
 */
export function parseTelegramBlock(body) {
  if (typeof body !== 'string') {
    return { text: '', actions: [], media: [] };
  }

  const actions = [];
  const media = [];

  // ── <actions> ────────────────────────────────────────────────────
  let stripped = body.replace(ACTIONS_RE, (_full, json) => {
    try {
      const parsed = JSON.parse(json.trim());
      if (Array.isArray(parsed)) {
        for (const item of parsed) {
          if (item && typeof item === 'object' && typeof item.label === 'string') {
            actions.push({
              label: String(item.label).slice(0, 64),
              value: String(item.value ?? item.label).slice(0, 256),
            });
          }
        }
      }
    } catch { /* drop block silently — agent's JSON was malformed */ }
    return '';
  });

  // ── <image> ──────────────────────────────────────────────────────
  stripped = stripped.replace(IMAGE_RE, (_full, attrs, caption) => {
    const a = parseAttributes(attrs);
    if (a.src) {
      media.push({
        kind: 'image',
        src: a.src,
        caption: (caption || '').trim(),
        alt: a.alt || null,
        type: null,
      });
    }
    return '';
  });

  // ── <attachment> ─────────────────────────────────────────────────
  stripped = stripped.replace(ATTACHMENT_RE, (_full, attrs, caption) => {
    const a = parseAttributes(attrs);
    if (a.src) {
      media.push({
        kind: 'document',
        src: a.src,
        caption: (caption || '').trim(),
        alt: null,
        type: a.type || null,
      });
    }
    return '';
  });

  return {
    text: stripped.replace(/\n{3,}/g, '\n\n').trim(),
    actions,
    media,
  };
}

export default { parseTelegramBlock };
