/**
 * Bundles every authored data file under `data/` into a single embedded module,
 * `src/data/bundle.generated.ts`.
 *
 * The bundle is inlined as an escaped JSON *string* that is `JSON.parse`d at load
 * time (mirroring the Rust crate's `include_str!`): tsc typechecks it instantly
 * (it is just a string), it parses once at import, and it compiles into `dist`
 * with no runtime filesystem access — so the published package works in Node,
 * bundlers, and browsers alike, where `data/` is not shipped.
 *
 * Run via `npm run codegen:data`. The output is gitignored and regenerated on
 * build/test/pack.
 */
import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { emptyRawData } from "./data/types.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = resolve(__dirname, "../..");
const DATA_ROOTS = [join(REPO_ROOT, "data", "core"), join(REPO_ROOT, "data", "enrichment")];
const OUT_FILE = join(__dirname, "data", "bundle.generated.ts");
/** Directory names that hold examples/scratch data and must never be bundled. */
const EXCLUDED_DIRS = new Set(["_example", "_port-audit"]);
/** Map a data file's base name (sans `.json`) to its `RawData` collection key. */
const FILE_TO_COLLECTION = {
    units: "units",
    weapons: "weapons",
    "weapon-keywords": "weaponKeywords",
    factions: "factions",
    abilities: "abilities",
    "phase-mappings": "phaseMappings",
    detachments: "detachments",
    stratagems: "stratagems",
    enhancements: "enhancements",
    "leader-attachments": "leaderAttachments",
    "unit-compositions": "unitCompositions",
    "wargear-options": "wargearOptions",
    wargear: "wargear",
    "game-versions": "gameVersions",
    missions: "missions",
    "mission-matchups": "missionMatchups",
    "secondary-cards": "missionCards",
    "deployment-patterns": "deploymentPatterns",
    "force-dispositions": "forceDispositions",
    "terrain-templates": "terrainTemplates",
    "terrain-layouts": "terrainLayouts",
    "resource-pools": "resourcePools",
    "timing-flags": "timingFlags",
    "interaction-flags": "interactionFlags",
};
/** The id-bearing key for a collection, used only for duplicate-id reporting. */
const ID_KEY = {
    abilities: "ability_id",
};
/** Recursively collect bundleable `.json` files, skipping excluded dirs/examples. */
function collectFiles(dir) {
    const out = [];
    for (const entry of readdirSync(dir)) {
        if (EXCLUDED_DIRS.has(entry))
            continue;
        const full = join(dir, entry);
        if (statSync(full).isDirectory()) {
            out.push(...collectFiles(full));
        }
        else if (entry.endsWith(".json") && !entry.endsWith(".example.json")) {
            out.push(full);
        }
    }
    return out;
}
function baseName(file) {
    return file.slice(file.lastIndexOf("/") + 1, -".json".length);
}
function build() {
    const data = emptyRawData();
    for (const root of DATA_ROOTS) {
        for (const file of collectFiles(root)) {
            const collection = FILE_TO_COLLECTION[baseName(file)];
            if (!collection)
                continue; // schema/scratch json we don't bundle
            const parsed = JSON.parse(readFileSync(file, "utf-8"));
            if (!Array.isArray(parsed)) {
                throw new Error(`expected a JSON array in ${file}`);
            }
            data[collection].push(...parsed);
        }
    }
    return data;
}
/** Warn (do not fail) on duplicate primary ids — a data-hygiene signal. */
function reportDuplicateIds(data) {
    for (const [collection, key] of Object.entries(ID_KEY)) {
        const seen = new Set();
        const dupes = new Set();
        for (const item of data[collection]) {
            const id = item[key];
            if (id === undefined)
                continue;
            if (seen.has(id))
                dupes.add(id);
            else
                seen.add(id);
        }
        if (dupes.size > 0) {
            console.warn(`  ⚠ ${collection}: ${dupes.size} duplicate ${key}(s), e.g. ${[...dupes].slice(0, 3).join(", ")}`);
        }
    }
}
function emit(data) {
    // JSON.stringify of the JSON text yields a valid, fully-escaped JS string
    // literal — safe to drop straight into the generated source.
    const jsonText = JSON.stringify(data);
    const literal = JSON.stringify(jsonText);
    return `/* GENERATED by 'npm run codegen:data' from the repository's data/ tree. DO NOT EDIT BY HAND. */
import type { RawData } from "./types.js";

const JSON_TEXT = ${literal};

/** The full 40kdc dataset, embedded at build time and parsed once at load. */
export const RAW_DATA: RawData = JSON.parse(JSON_TEXT) as RawData;
`;
}
function main() {
    const data = build();
    reportDuplicateIds(data);
    writeFileSync(OUT_FILE, emit(data));
    const counts = Object.keys(data)
        .map((k) => `${k}=${data[k].length}`)
        .join(", ");
    console.log(`Wrote ${OUT_FILE}\n  ${counts}`);
}
main();
//# sourceMappingURL=codegen-data.js.map