const CALLOUT_LABELS = new Map([
  ["aside", "Aside"],
  ["caution", "Caution"],
  ["note", "Note"],
  ["tip", "Tip"],
  ["warning", "Warning"],
]);

function textFromInline(node) {
  if (!node) return "";
  if (typeof node.value === "string") return node.value;
  if (!Array.isArray(node.children)) return "";
  return node.children.map(textFromInline).join("");
}

function normalizeLabel(value) {
  return value
    .trim()
    .replace(/^["“”]+|["“”]+$/g, "")
    .replace(/:$/, "")
    .toLowerCase();
}

function paragraphCalloutLabel(paragraph) {
  if (paragraph?.type !== "paragraph") return null;

  const first = paragraph.children?.[0];
  if (first?.type === "text") {
    const match = first.value.match(/^\s*\[!(aside|caution|note|tip|warning)\]\s*/i);
    if (match) {
      return CALLOUT_LABELS.get(normalizeLabel(match[1]));
    }
  }

  if (first?.type !== "strong") return null;

  const label = CALLOUT_LABELS.get(normalizeLabel(textFromInline(first)));
  if (!label) return null;

  return label;
}

function removeLeadingLabel(paragraph, label) {
  if (paragraph?.type !== "paragraph") return;

  const first = paragraph.children?.[0];
  if (first?.type === "text") {
    first.value = first.value.replace(/^\s*\[!(aside|caution|note|tip|warning)\]\s*/i, "");
    if (!first.value) paragraph.children.shift();
    return;
  }

  const firstText = normalizeLabel(textFromInline(first));
  if (!first || first.type !== "strong" || firstText !== label.toLowerCase()) {
    return;
  }

  paragraph.children.shift();

  const next = paragraph.children[0];
  if (next?.type === "text") {
    next.value = next.value.replace(/^:\s*/, "").replace(/^\s+/, "");
    if (!next.value) paragraph.children.shift();
  }
}

function transformBlockquote(node) {
  if (node.type !== "blockquote") return;

  const first = node.children?.[0];
  const label = paragraphCalloutLabel(first);
  if (!label) return;

  removeLeadingLabel(first, label);
  if (first.children?.length === 0) {
    node.children.shift();
  }

  node.data = {
    hName: "aside",
    hProperties: {
      className: ["callout", `callout-${label.toLowerCase()}`],
      dataCalloutLabel: label,
    },
  };
}

function visit(node) {
  transformBlockquote(node);

  if (!Array.isArray(node.children)) return;
  for (const child of node.children) {
    visit(child);
  }
}

export default function remarkCallouts() {
  return function transformer(tree) {
    visit(tree);
  };
}
