// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

// cSpell:ignore Doxyfile

// Orchestrates the API-reference generation: generate the cbindgen C++ headers
// (via `cargo xtask generate_cppdocs_headers`), run Doxygen (XML only) over the
// C++ headers, then convert that XML into Markdown for Starlight. This is the
// C++ analogue of the `starlight-typedoc` plugin used by the Node.js docs.

import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { createHighlighter } from "shiki";
import { DoxygenConverter } from "./lib/doxygen.ts";
import { API_ROOT } from "./lib/slug.ts";
import { mkdirSync, rmSync, writeFileSync } from "node:fs";

const scriptsDir = dirname(fileURLToPath(import.meta.url));
const docsRoot = join(scriptsDir, "..");
const repoRoot = join(docsRoot, "..", "..");
const xmlDir = join(repoRoot, "target", "cppdocs", "xml");
const contentDocs = join(docsRoot, "src", "content", "docs");
// The API-reference sidebar, organized by namespace, is generated alongside the
// pages and imported by astro.config.mjs. It lives outside the content
// collection so Astro does not treat it as a page.
const sidebarFile = join(docsRoot, "src", "api-sidebar.generated.mjs");

// Generate the cbindgen headers and point Doxygen at them. The Doxyfile reads
// the include directory from $(SLINT_CPP_GENERATED_INCLUDE), which we set here
// so the whole pipeline runs from a single `gen:api`. If the variable is set
// externally we trust it and skip the xtask — an escape hatch for environments
// that pre-generate the headers or have no Rust toolchain.
function generateHeaders(): void {
    if (process.env.SLINT_CPP_GENERATED_INCLUDE) {
        return;
    }
    // Experimental APIs are opt-in: snapshot/CI builds set this, releases don't.
    const experimental = process.env.SLINT_CPP_DOCS_EXPERIMENTAL
        ? ["--experimental"]
        : [];
    const result = spawnSync(
        "cargo",
        ["xtask", "generate_cppdocs_headers", ...experimental],
        { cwd: repoRoot, stdio: "inherit" },
    );
    if (result.error) {
        throw new Error(
            "Could not run `cargo xtask generate_cppdocs_headers`. Install a Rust " +
                "toolchain, or pre-generate the headers and point " +
                "SLINT_CPP_GENERATED_INCLUDE at them.",
        );
    }
    if (result.status !== 0) {
        process.exit(result.status ?? 1);
    }
    process.env.SLINT_CPP_GENERATED_INCLUDE = join(
        repoRoot,
        "target",
        "cppdocs",
        "generated_include",
    );
}

function runDoxygen(): void {
    const result = spawnSync("doxygen", ["Doxyfile"], {
        cwd: docsRoot,
        stdio: "inherit",
    });
    if (result.error) {
        throw new Error(
            "Could not run `doxygen`. Install it (e.g. `apt-get install doxygen`) " +
                "or pre-generate the XML at target/cppdocs/xml.",
        );
    }
    if (result.status !== 0) {
        process.exit(result.status ?? 1);
    }
}

async function convert(): Promise<void> {
    if (!existsSync(join(xmlDir, "index.xml"))) {
        throw new Error(`No Doxygen XML at ${xmlDir} (expected index.xml).`);
    }
    const apiDir = join(contentDocs, API_ROOT);
    rmSync(apiDir, { recursive: true, force: true });
    const highlighter = await createHighlighter({
        themes: ["light-plus", "dark-plus"],
        langs: ["cpp"],
    });
    const { pages, sidebar } = new DoxygenConverter(xmlDir, {
        highlighter,
    }).convert();
    for (const page of pages) {
        const file = join(contentDocs, `${page.slug}.md`);
        mkdirSync(dirname(file), { recursive: true });
        writeFileSync(file, page.markdown, "utf8");
    }
    // REUSE-IgnoreStart — the header below is written into the generated file,
    // it is not the license of this script.
    const sidebarHeader = [
        "// Copyright © SixtyFPS GmbH <info@slint.dev>",
        "// SPDX-License-Identifier: MIT",
        "",
        "// Generated by `pnpm gen:api`; do not edit. Imported by astro.config.mjs.",
        "",
    ].join("\n");
    // REUSE-IgnoreEnd
    writeFileSync(
        sidebarFile,
        `${sidebarHeader}export default ${JSON.stringify(sidebar, null, 4)};\n`,
        "utf8",
    );
    console.log(`Generated ${pages.length} C++ API page(s) into ${apiDir}`);
}

generateHeaders();
runDoxygen();
await convert();
