/**
 * Portable-text.ts
 *
 * Converts Sanity Portable Text `content` arrays into well-structured
 * Markdown using @portabletext/markdown. Includes custom renderers for the
 * doc-platform's bespoke block types.
 *
 * Block type inventory (content field):
 *
 *   Standard:       block
 *   Code:           codeBlock
 *   Tables:         propertiesTable, tableBlock, methodTable
 *   Callouts:       docsCallout
 *   Cards/Links:    docsCardCollection, docsCallToAction
 *   Media:          image, muxVideo, youtube, youtubePlaylist, codesandbox
 *   Other:          docsPaidFeature, button, methodHeading
 */
import { DefaultHorizontalRuleRenderer, DefaultImageRenderer, portableTextToMarkdown, } from "@portabletext/markdown";
// ---------------------------------------------------------------------------
// Recursive helper – convert nested PT to markdown
// ---------------------------------------------------------------------------
/**
 * Convert a Sanity Portable Text `content` array to well-structured Markdown.
 */
export function toMarkdown(blocks) {
    if (!blocks || blocks.length === 0) {
        return "";
    }
    return portableTextToMarkdown(blocks, {
        // --- Custom mark renderers ---
        marks: {
            link: renderLink,
        },
        // --- Custom type renderers ---
        types: {
            button: () => "",
            // Code
            codeBlock: renderCodeBlock,
            codesandbox: ({ value }) => {
                const v = value;
                const url = v.url ?? "";
                return url ? `[CodeSandbox](${url})` : "*[CodeSandbox]*";
            },
            // Callouts
            docsCallout: renderDocsCallout,
            docsCallToAction: ({ value }) => {
                const title = value.title;
                return title ? `**${title}**` : "";
            },
            // Cards / CTAs
            docsCardCollection: renderDocsCardCollection,
            // Skip / minimal rendering
            docsPaidFeature: () => "",
            "horizontal-rule": DefaultHorizontalRuleRenderer,
            // Media — render a placeholder rather than dropping silently
            image: DefaultImageRenderer,
            methodHeading: renderMethodHeading,
            methodTable: renderPropertiesTable, // Same shape
            muxVideo: () => "*[Video]*",
            // Tables
            propertiesTable: renderPropertiesTable,
            tableBlock: renderTableBlock,
            youtube: ({ value }) => {
                const v = value;
                const url = v.url ?? "";
                return url ? `[Video](${url})` : "*[Video]*";
            },
            youtubePlaylist: () => "*[YouTube Playlist]*",
        },
    });
}
// ---------------------------------------------------------------------------
// Custom type renderers
// ---------------------------------------------------------------------------
function nestedToMarkdown(blocks) {
    if (!blocks || blocks.length === 0) {
        return "";
    }
    return toMarkdown(blocks);
}
/**
 * CodeBlock
 * Shape: { blocks: [{ code: { code, language }, filename }] }
 *
 * May contain multiple tabs (e.g. "Action" / "Mutation").
 */
function renderCodeBlock({ value, }) {
    const blocks = (value.blocks ?? []);
    return blocks
        .map((b) => {
        const code = b.code?.code ?? "";
        const lang = b.code?.language ?? "";
        const filename = b.filename ? ` (${b.filename})` : "";
        return `\`\`\`${lang}${filename}\n${code}\n\`\`\``;
    })
        .join("\n\n");
}
/**
 * DocsCallout
 * Shape: { type: "warning"|"info"|..., title?: string, content: PT[] }
 */
function renderDocsCallout({ value, }) {
    const type = value.type ?? "info";
    const title = value.title;
    const inner = nestedToMarkdown(value.content);
    const label = title ?? type.charAt(0).toUpperCase() + type.slice(1);
    const lines = inner.split("\n").map((l) => `> ${l}`);
    return `> **${label}**\n>\n${lines.join("\n")}`;
}
/**
 * DocsCardCollection
 * Shape (after GROQ reference resolution):
 *   { cards: [{ title?, description?, resolvedTitle?, resolvedSlug?, url? }] }
 */
function renderDocsCardCollection({ value, }) {
    const cards = (value.cards ?? []);
    if (cards.length === 0) {
        return "";
    }
    const rendered = cards
        .map((c) => {
        // oxlint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- empty strings should fall through to next candidate
        const title = c.title || c.resolvedTitle || c.description;
        if (!title) {
            return null;
        } // Skip cards with no meaningful content
        const url = c.resolvedSlug
            ? `https://sanity.io/docs/${c.resolvedSlug}`
            : (c.url ?? "");
        const desc = c.description && c.description !== title ? ` — ${c.description}` : "";
        return url ? `- [${title}](${url})${desc}` : `- **${title}**${desc}`;
    })
        .filter(Boolean);
    return rendered.join("\n");
}
function renderLink({ children, value, }) {
    const href = value?.url ?? value?.href ?? "";
    return href ? `[${children}](${href})` : children;
}
/**
 * MethodTable / methodHeading — API method documentation
 */
function renderMethodHeading({ value, }) {
    const name = value.name ?? "";
    const signature = value.signature ?? "";
    return signature ? `### \`${name}(${signature})\`` : `### \`${name}\``;
}
// ---------------------------------------------------------------------------
// Link mark renderer
//
// Sanity docs use `url` in the content field's link annotations.
// The library default only handles `href`. This handles `url` as well.
// ---------------------------------------------------------------------------
/**
 * PropertiesTable
 * Shape: { properties: [{ name, propertyDataType, explanation: PT[], isRequired? }] }
 */
function renderPropertiesTable({ value, }) {
    const properties = (value.properties ?? []);
    if (properties.length === 0) {
        return "";
    }
    const rows = properties.map((p) => {
        const name = p.name ?? "";
        const type = p.propertyDataType ?? "";
        const required = p.isRequired ? " **(required)**" : "";
        const explanation = nestedToMarkdown(p.explanation)
            .replace(/\n/g, " ")
            .trim();
        return `| \`${name}\`${required} | \`${type}\` | ${explanation} |`;
    });
    return [
        "| Property | Type | Description |",
        "|----------|------|-------------|",
        ...rows,
    ].join("\n");
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * TableBlock
 * Shape: { headerRow: boolean, title?: string, table: { rows: [{ cells: string[] }] } }
 */
function renderTableBlock({ value, }) {
    const title = value.title;
    const headerRow = value.headerRow;
    const table = value.table;
    const rows = table?.rows ?? [];
    if (rows.length === 0) {
        return "";
    }
    const lines = [];
    if (title) {
        lines.push(`**${title}**\n`);
    }
    rows.forEach((row, i) => {
        const cells = (row.cells ?? []).map((c) => c.replace(/\|/g, "\\|"));
        lines.push(`| ${cells.join(" | ")} |`);
        if (i === 0 && headerRow) {
            lines.push(`| ${cells.map(() => "---").join(" | ")} |`);
        }
    });
    // If no header row marker yet, add one after the first row anyway
    // So the markdown table is valid
    if (!headerRow && rows.length > 0) {
        const firstCells = rows[0].cells ?? [];
        lines.splice(1, 0, `| ${firstCells.map(() => "---").join(" | ")} |`);
    }
    return lines.join("\n");
}
