import { nip19 } from "nostr-tools";

// Matches NIP-27 `nostr:` references (and bare bech32 entities) embedded in
// article text: npub/nprofile (mentions) and note/nevent/naddr (notes).
const NOSTR_RE =
  /(?:nostr:)?((?:npub|nprofile|note|nevent|naddr)1[023456789acdefghjklmnpqrstuvwxyz]+)/gi;

function isValidEntity(bech32: string): boolean {
  try {
    const { type } = nip19.decode(bech32);
    return ["npub", "nprofile", "note", "nevent", "naddr"].includes(type);
  } catch {
    return false;
  }
}

// Splits a plain-text value into text + `link` nodes, where each link's url is
// `nostr:<bech32>`. The markdown `a` renderer detects that scheme and swaps in
// the live profile/note component.
function splitText(value: string): any[] {
  const out: any[] = [];
  let last = 0;
  NOSTR_RE.lastIndex = 0;
  let m: RegExpExecArray | null;
  while ((m = NOSTR_RE.exec(value)) !== null) {
    const bech32 = m[1];
    if (!isValidEntity(bech32)) continue;
    if (m.index > last) {
      out.push({ type: "text", value: value.slice(last, m.index) });
    }
    out.push({
      type: "link",
      url: `nostr:${bech32}`,
      children: [{ type: "text", value: m[0] }],
    });
    last = m.index + m[0].length;
  }
  if (out.length === 0) return [{ type: "text", value }];
  if (last < value.length) out.push({ type: "text", value: value.slice(last) });
  return out;
}

// Remark plugin: walk the mdast tree and replace `nostr:` tokens inside text
// nodes. Skips code and existing links so identifiers there are left untouched.
export function remarkNostr() {
  return (tree: any) => {
    const walk = (node: any) => {
      if (!node || !Array.isArray(node.children)) return;
      const next: any[] = [];
      for (const child of node.children) {
        if (child.type === "text") {
          next.push(...splitText(child.value));
          continue;
        }
        if (child.type !== "link" && child.type !== "inlineCode" && child.type !== "code") {
          walk(child);
        }
        next.push(child);
      }
      node.children = next;
    };
    walk(tree);
  };
}
