/**
 * Parses Figma metadata XML, maps picto names to ui-kit conventions,
 * and prepares gradient asset exports.
 *
 * Usage:
 *   bun tools/picto-sync.ts parse <metadata.xml>          # build mapping JSON
 *   bun tools/picto-sync.ts optimize <png-dir>             # convert PNGs to WebP
 *   bun tools/picto-sync.ts generate-imports <mapping.json> # update pictos.ts entries
 *
 * See also: bun tools/picto-verify-gradients.ts — compare glass vs gradient silhouettes
 */

import { execSync } from "node:child_process";
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
import { basename, join } from "node:path";

const PICTOS_DIR = join(import.meta.dir, "../src/assets/pictos");
const MAPPING_PATH = join(import.meta.dir, "../src/constants/picto-figma-map.json");
const EXPORT_NODES_PATH = join(import.meta.dir, "vector-export-nodes.json");
const INTEGRATION_EXPORTS_PATH = join(import.meta.dir, "integration-gradient-exports.json");
const DUPLICATE_VECTOR_MAPPINGS_PATH = join(import.meta.dir, "duplicate-vector-mappings.json");
const PNG_DIR = join(import.meta.dir, "../.picto-gradient-png");

type VectorExportNode = {
    symbolId: string;
    exportNodeId: string;
    exportNodeName: string;
    exportNodeType?: string;
};

type PictoMapping = {
    figmaFrameName: string;
    figmaLightName: string;
    figmaVectorName: string;
    figmaLightNodeId: string;
    figmaVectorNodeId: string;
    figmaVectorExportNodeId: string;
    uiKitFileName: string;
    uiKitExportName: string;
    uiKitGradientFileName: string;
    uiKitGradientExportName: string;
    matchMethod: "exact" | "normalized" | "manual" | "image";
};

/** Manual overrides where Figma names diverge significantly from ui-kit filenames. */
const MANUAL_FRAME_TO_FILE: Record<string, string> = {
    "Pension Dollar": "Pension",
    "Speed / Performance": "Performance",
    "Code blob (JSON)": "JSON",
    "Full-Text Search": "Full-TextSearch",
    "Real time applications": "RealTimeApplications",
    "Knowledge graph": "KnowledgeGraph",
    "Instance version": "InstanceVersion",
    "Fraud detection": "FraudDetection",
    "Embedded edge": "EmbeddedEdge",
    "Embedded devices": "EmbeddedDevices",
    "Machine Learning Brain": "MachineLearning",
    Flexible_time: "Flexible",
    Learn: "Tutorials",
    "Tiny asset / thumbnail": "Thumbnail",
    "Inclusive communication": "InclusiveCommunication",
    "regulatory compliance": "RegulatoryCompliance",
    "Conflict detection": "ConflictDetection",
    "Health Chat": "HealthChat",
    "Video file": "VideoFile",
    "Padlock Closed": "PadlockClosed",
    "Padlock Open": "PadlockOpen",
    "Two Circles": "TwoCircles",
    "Pie Chart": "PieChart",
    "Arrow Up": "ArrowUp",
    "Arrow Down": "ArrowDown",
    "Play Pause": "PlayPause",
    "Price Tag": "PriceTag",
    "Key Value": "KeyValue",
    "Finger Prints": "FingerPrints",
    "Hands On": "HandsOn",
    "Claims Management": "ClaimsManagement",
    "Mental Health": "MentalHealth",
    "Generative AI": "GenerativeAI",
    "Vector Search": "VectorSearch",
    "Document Search": "DocumentSearch",
    "Reference guides": "Reference-guides",
    "5 Stars": "5Stars",
    "10": "2106",
    "2112": "2106",
    "360 Degree View": "360DegreeView",
    "Save Floppy Disc": "SaveFloppyDisc",
    "Hard Disc": "HardDisc",
    "Storage / Hard Disc": "HardDisc",
    "On-disk": "OnDisk",
    "Labs / Atom": "Atom",
    "Image (photo)": "Image",
    "Stack Overflow": "StackOverflow",
    YouTube: "Youtube",
    WebAssembly: "Wasm",
    n8n: "N8n",
    "Man In Circle": "ManInCircle",
    "Health Bubble": "HealthBubble",
    "Health Insurance": "HealthInsurance",
    "Home Setup": "HomeSetup",
    "Cloud Nodes": "CloudNodes",
    "Merge Queue": "MergeQueue",
    "Commit Queue": "CommitQueue",
    "Custom API Endpoints": "CustomAPIEndpoints",
    "Portfolio Risk Management": "PortfolioRiskManagement",
    "Personalized Policy": "PersonalizedPolicy",
    "Problem Danger Error": "ProblemDangerError",
    "Operational Readiness": "OperationalReadiness",
    "Integration Support": "IntegrationSupport",
    "Dedicated Support": "DedicatedSupport",
    "Secure Document": "SecureDocument",
    "Visual Studio Code": "VisualStudioCode",
    "Node Brain": "NodeBrain",
    "Drag Cursor": "DragCursor",
    "Audio Clip": "AudioClip",
    "Media File": "MediaFile",
    "Create Plus": "Create",
    "Document / PDF": "PDF",
    "Document PDF": "PDF",
    "Agent / Bot": "Bot",
    "cloud nodes": "CloudNodes",
    "Commit queue": "CommitQueue",
    "Merge queue": "MergeQueue",
    "JSON File": "JSONFile",
    "Video File": "VideoFile",
    "Time Series": "TimeSeries",
    "SQL Backends": "SQLBackends",
    "Full Text Search": "Full-TextSearch",
    "Badge Access": "BadgeAccess",
    "Quotation Marks": "QuotationMarks",
    "Thumbs Up": "ThumbsUp",
    "Thumbs Down": "ThumbsDown",
    "Zoom In": "ZoomIn",
    "SDB Cloud": "SDBCloud",
    "YU App": "YUApp",
};

/** Storybook frame names that differ from the Figma Icons section frame name. */
const MANUAL_STORYBOOK_FRAME_TO_FIGMA_FRAME: Record<string, string> = {
    "2112": "10",
};

/** Vector symbol names can be truncated or differ from frame names. */
const MANUAL_VECTOR_TO_FRAME: Record<string, string> = {
    "Inclusive commu_vector": "Inclusive communication",
    Flexible_vector: "Flexible",
    "Code blob (JSON)_vector": "Code blob (JSON)",
    "Speed / Performance_vector": "Speed / Performance",
    "Pension Dollar_vector": "Pension Dollar",
    "Real time applications_vector": "Real time applications",
    "Knowledge graph_vector": "Knowledge graph",
    "Instance version_vector": "Instance version",
    "Fraud detection_vector": "Fraud detection",
    "Embedded edge_vector": "Embedded edge",
    "Embedded devices_vector": "Embedded devices",
    "Machine Learning Brain_vector": "Machine Learning Brain",
    Learn_vector: "Learn",
    "Tiny asset / thumbnail_vector": "Tiny asset / thumbnail",
    "regulatory compliance_vector": "regulatory compliance",
    "Conflict detection_vector": "Conflict detection",
    "Document Search_vector": "Document Search",
    "Reference guides_vector": "Reference guides",
    "Health Chat_vector": "Health Chat",
    "Video file_vector": "Video file",
    "Padlock Closed_vector": "Padlock Closed",
    "Padlock Open_vector": "Padlock Open",
    "Two Circles_vector": "Two Circles",
    "Pie Chart_vector": "Pie Chart",
    "Arrow Up_vector": "Arrow Up",
    "Arrow Down_vector": "Arrow Down",
    "Play Pause_vector": "Play Pause",
    "Price Tag_vector": "Price Tag",
    "Key Value_vector": "Key Value",
    "Finger Prints_vector": "Finger Prints",
    "Hands On_vector": "Hands On",
    "Claims Management_vector": "Claims Management",
    "Mental Health_vector": "Mental Health",
    "Generative AI_vector": "Generative AI",
    "Vector Search_vector": "Vector Search",
    "5 Stars_vector": "5 Stars",
    "Full-Text Search_vector": "Full-Text Search",
    embedding_vector: "embeddingg",
    "Storage / Hard Disc_vector": "Storage / Hard Disc",
    "On-disk_vector": "On-disk",
    "Labs / Atom_vector": "Labs / Atom",
    "Image (photo)_vector": "Image (photo)",
    "reference-guides_vector": "reference-guides",
    "Group 14303_vector": "Begin",
};

/** ui-kit export names that diverge from fileNameToExportName(). */
const EXPORT_NAME_OVERRIDES: Record<string, string> = {
    "Reference-guides": "pictoReferenceGuides",
};

function normalizeFrameName(name: string): string {
    return name
        .replace(/\s*\/\s*/g, " ")
        .replace(/\s+/g, " ")
        .trim();
}

function frameNameToFileName(frameName: string): string {
    const manual = MANUAL_FRAME_TO_FILE[frameName];
    if (manual) return manual;

    // Preserve intentional hyphens (e.g. Full-Text Search)
    if (frameName.includes("-")) {
        return frameName
            .split(" ")
            .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
            .join("");
    }

    // Default: PascalCase without spaces
    return frameName
        .split(/[\s_]+/)
        .filter(Boolean)
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
        .join("");
}

function fileNameToExportName(fileName: string): string {
    const override = EXPORT_NAME_OVERRIDES[fileName];
    if (override) return override;

    const base = fileName.replace(/[^a-zA-Z0-9]/g, "");
    if (/^\d/.test(base)) return `picto${base}`;
    return `picto${base.charAt(0).toUpperCase()}${base.slice(1)}`;
}

type IntegrationGradientExport = {
    uiKitFileName: string;
    exportNodeId: string;
    figmaFrameName: string;
};

type DuplicateVectorMapping = {
    uiKitFileName: string;
    symbolId: string;
    exportNodeId: string;
    figmaVectorName: string;
    figmaFrameName?: string;
    figmaLightNodeId?: string;
};

async function loadIntegrationExports(): Promise<IntegrationGradientExport[]> {
    try {
        return JSON.parse(await readFile(INTEGRATION_EXPORTS_PATH, "utf8"));
    } catch {
        return [];
    }
}

async function loadDuplicateVectorMappings(): Promise<DuplicateVectorMapping[]> {
    try {
        return JSON.parse(await readFile(DUPLICATE_VECTOR_MAPPINGS_PATH, "utf8"));
    } catch {
        return [];
    }
}

function parseMetadata(xml: string) {
    const storybookFrames = new Map<
        string,
        { frameId: string; lightName: string; lightNodeId: string }
    >();
    const vectorSymbols = new Map<string, { vectorName: string; vectorNodeId: string }>();

    const frameRegex =
        /<frame id="([^"]+)" name="([^"]+)"[^>]*>\s*<instance id="([^"]+)" name="([^"]+)_light"/g;
    for (const match of xml.matchAll(frameRegex)) {
        const [, , frameName, lightNodeId, lightBaseName] = match;
        const normalized = normalizeFrameName(frameName);
        if (!storybookFrames.has(normalized)) {
            storybookFrames.set(normalized, {
                frameId: match[1],
                lightName: `${lightBaseName}_light`,
                lightNodeId,
            });
        }
    }

    const symbolRegex = /<symbol id="([^"]+)" name="([^"]+_vector)"/g;
    for (const match of xml.matchAll(symbolRegex)) {
        const [, vectorNodeId, vectorName] = match;
        if (!vectorSymbols.has(vectorName)) {
            vectorSymbols.set(vectorName, { vectorName, vectorNodeId });
        }
    }

    return { storybookFrames, vectorSymbols };
}

function vectorNameToFrameName(vectorName: string): string {
    if (MANUAL_VECTOR_TO_FRAME[vectorName]) {
        return MANUAL_VECTOR_TO_FRAME[vectorName];
    }
    return normalizeFrameName(vectorName.replace(/_vector$/, ""));
}

async function loadExportNodes(): Promise<Record<string, VectorExportNode>> {
    try {
        return JSON.parse(await readFile(EXPORT_NODES_PATH, "utf8"));
    } catch {
        return {};
    }
}

async function buildMapping(xmlPath: string): Promise<PictoMapping[]> {
    const xml = await readFile(xmlPath, "utf8");
    const exportNodes = await loadExportNodes();
    const repoFiles = new Set(
        (await readdir(PICTOS_DIR))
            .filter((f) => f.endsWith(".webp") && !f.endsWith("Gradient.webp"))
            .map((f) => f.replace(/\.webp$/, "")),
    );
    const { storybookFrames, vectorSymbols } = parseMetadata(xml);

    const vectorsByFrame = new Map<string, { vectorName: string; vectorNodeId: string }>();
    for (const [, vector] of vectorSymbols) {
        const frameName = vectorNameToFrameName(vector.vectorName);
        if (!vectorsByFrame.has(frameName)) {
            vectorsByFrame.set(frameName, vector);
        }
    }

    const mappings: PictoMapping[] = [];

    for (const [frameName, light] of storybookFrames) {
        const figmaFrameName = MANUAL_STORYBOOK_FRAME_TO_FIGMA_FRAME[frameName] ?? frameName;
        const vector = vectorsByFrame.get(figmaFrameName);
        if (!vector) continue;

        let uiKitFileName = frameNameToFileName(figmaFrameName);
        let matchMethod: PictoMapping["matchMethod"] = "normalized";

        if (MANUAL_FRAME_TO_FILE[figmaFrameName] || MANUAL_FRAME_TO_FILE[frameName]) {
            matchMethod = "manual";
        } else if (repoFiles.has(uiKitFileName)) {
            matchMethod = "exact";
        } else {
            // Try case-insensitive match
            const found = [...repoFiles].find(
                (f) => f.toLowerCase() === uiKitFileName.toLowerCase(),
            );
            if (found) {
                uiKitFileName = found;
                matchMethod = "exact";
            }
        }

        if (!repoFiles.has(uiKitFileName)) continue;

        const exportNode = exportNodes[vector.vectorName];
        mappings.push({
            figmaFrameName: figmaFrameName,
            figmaLightName: light.lightName,
            figmaVectorName: vector.vectorName,
            figmaLightNodeId: light.lightNodeId,
            figmaVectorNodeId: vector.vectorNodeId,
            figmaVectorExportNodeId: exportNode?.exportNodeId ?? vector.vectorNodeId,
            uiKitFileName,
            uiKitExportName: fileNameToExportName(uiKitFileName),
            uiKitGradientFileName: `${uiKitFileName}Gradient`,
            uiKitGradientExportName: `${fileNameToExportName(uiKitFileName)}Gradient`,
            matchMethod,
        });
    }

    // Include vectors that exist but weren't in storybook (from Icons section only)
    for (const [vectorName, vector] of vectorSymbols) {
        const frameName = vectorNameToFrameName(vectorName);
        if (storybookFrames.has(frameName)) continue;

        const uiKitFileName = frameNameToFileName(frameName);
        if (!repoFiles.has(uiKitFileName)) continue;
        if (mappings.some((m) => m.uiKitFileName === uiKitFileName)) continue;

        const exportNode = exportNodes[vector.vectorName];
        mappings.push({
            figmaFrameName: frameName,
            figmaLightName: `${frameName.replace(/\s+/g, " ")}_light`,
            figmaVectorName: vector.vectorName,
            figmaLightNodeId: "",
            figmaVectorNodeId: vector.vectorNodeId,
            figmaVectorExportNodeId: exportNode?.exportNodeId ?? vector.vectorNodeId,
            uiKitFileName,
            uiKitExportName: fileNameToExportName(uiKitFileName),
            uiKitGradientFileName: `${uiKitFileName}Gradient`,
            uiKitGradientExportName: `${fileNameToExportName(uiKitFileName)}Gradient`,
            matchMethod: "manual",
        });
    }

    const integrationExports = await loadIntegrationExports();
    for (const entry of integrationExports) {
        if (!repoFiles.has(entry.uiKitFileName)) continue;
        if (mappings.some((m) => m.uiKitFileName === entry.uiKitFileName)) continue;

        mappings.push({
            figmaFrameName: entry.figmaFrameName,
            figmaLightName: `${entry.figmaFrameName}_light`,
            figmaVectorName: `${entry.figmaFrameName}_gradient`,
            figmaLightNodeId: "",
            figmaVectorNodeId: entry.exportNodeId,
            figmaVectorExportNodeId: entry.exportNodeId,
            uiKitFileName: entry.uiKitFileName,
            uiKitExportName: fileNameToExportName(entry.uiKitFileName),
            uiKitGradientFileName: `${entry.uiKitFileName}Gradient`,
            uiKitGradientExportName: `${fileNameToExportName(entry.uiKitFileName)}Gradient`,
            matchMethod: "manual",
        });
    }

    // Keep one mapping per ui-kit file (prefer exact frame name casing)
    const byFile = new Map<string, PictoMapping>();
    for (const mapping of mappings) {
        const existing = byFile.get(mapping.uiKitFileName);
        if (!existing) {
            byFile.set(mapping.uiKitFileName, mapping);
            continue;
        }
        const preferNew =
            mapping.figmaFrameName === mapping.uiKitFileName ||
            (existing.figmaFrameName !== existing.uiKitFileName &&
                mapping.figmaFrameName.length <= existing.figmaFrameName.length);
        if (preferNew) byFile.set(mapping.uiKitFileName, mapping);
    }

    const duplicateMappings = await loadDuplicateVectorMappings();
    for (const dup of duplicateMappings) {
        const hasGlass = repoFiles.has(dup.uiKitFileName);
        const isNewGlass = dup.uiKitFileName === "Compute";
        if (!hasGlass && !isNewGlass) continue;

        const existing = byFile.get(dup.uiKitFileName);
        byFile.set(dup.uiKitFileName, {
            figmaFrameName: dup.figmaFrameName ?? existing?.figmaFrameName ?? dup.uiKitFileName,
            figmaLightName: existing?.figmaLightName ?? `${dup.uiKitFileName}_light`,
            figmaVectorName: dup.figmaVectorName,
            figmaLightNodeId: dup.figmaLightNodeId ?? existing?.figmaLightNodeId ?? "",
            figmaVectorNodeId: dup.symbolId,
            figmaVectorExportNodeId: dup.exportNodeId,
            uiKitFileName: dup.uiKitFileName,
            uiKitExportName: fileNameToExportName(dup.uiKitFileName),
            uiKitGradientFileName: `${dup.uiKitFileName}Gradient`,
            uiKitGradientExportName: `${fileNameToExportName(dup.uiKitFileName)}Gradient`,
            matchMethod: "manual",
        });
    }

    return [...byFile.values()].sort((a, b) => a.uiKitFileName.localeCompare(b.uiKitFileName));
}

async function optimizePngDir(pngDir: string, outDir = PICTOS_DIR) {
    await mkdir(outDir, { recursive: true });
    const files = (await readdir(pngDir)).filter((f) => f.endsWith(".png"));

    for (const file of files) {
        const base = basename(file, ".png");
        const input = join(pngDir, file);
        const sized = join(pngDir, `.tmp-${base}.png`);
        const out = join(outDir, `${base}.webp`);
        // Two-step convert avoids ImageMagick WebP encoder hangs on some systems.
        // Trim export padding, then scale icon content to fill the 500×500 canvas.
        execSync(
            `magick "${input}" -trim +repage -resize 500x500 -background none -gravity center -extent 500x500 PNG32:"${sized}"`,
        );
        execSync(`magick "${sized}" -define webp:lossless=true "${out}"`);
        execSync(`rm -f "${sized}"`);
        console.log(`Optimized ${file} -> ${basename(out)}`);
    }
}

async function main() {
    const [command, arg] = process.argv.slice(2);

    if (command === "parse" && arg) {
        const mappings = await buildMapping(arg);
        const repoExports = [
            ...new Set(
                (await readFile(join(import.meta.dir, "../src/constants/pictos.ts"), "utf8"))
                    .match(/export \{[^}]+\}/s)?.[0]
                    .match(/picto[A-Za-z0-9]+/g) ?? [],
            ),
        ].filter((name) => name !== "pictos");
        const mappedExports = new Set(mappings.map((m) => m.uiKitExportName));
        const output = {
            figmaFileKey: "mcdcK62LxTlRHgolqxqLhf",
            figmaPageNodeId: "25770:1245",
            figmaPageUrl:
                "https://www.figma.com/design/mcdcK62LxTlRHgolqxqLhf/SurrealDB-Brand-Guidelines?node-id=25770-1245",
            description:
                "Maps Figma picto names (light + vector variants) to ui-kit export names. Gradient assets use the vector variant.",
            mappings,
            uiKitPictosWithoutGradient: repoExports.filter((name) => !mappedExports.has(name)),
        };
        await writeFile(MAPPING_PATH, `${JSON.stringify(output, null, 4)}\n`);
        console.log(`Wrote ${mappings.length} mappings to ${MAPPING_PATH}`);
        console.log(
            `${output.uiKitPictosWithoutGradient.length} ui-kit pictos have no gradient vector in Figma yet`,
        );
        return;
    }

    if (command === "optimize" && arg) {
        await optimizePngDir(arg);
        return;
    }

    if (command === "list-vector-ids" && arg) {
        const parsed = JSON.parse(await readFile(arg, "utf8"));
        const mappings: PictoMapping[] = Array.isArray(parsed) ? parsed : parsed.mappings;
        for (const m of mappings) {
            console.log(
                `${m.uiKitGradientFileName}\t${m.figmaVectorExportNodeId ?? m.figmaVectorNodeId}`,
            );
        }
        return;
    }

    if (command === "write-export-batches") {
        const parsed = JSON.parse(await readFile(MAPPING_PATH, "utf8"));
        const mappings: PictoMapping[] = parsed.mappings ?? parsed;
        await mkdir(PNG_DIR, { recursive: true });
        const batches: { fileName: string; nodeId: string }[][] = [];
        const items = mappings.map((m) => ({
            fileName: m.uiKitGradientFileName,
            nodeId: m.figmaVectorExportNodeId,
        }));
        for (let i = 0; i < items.length; i += 20) {
            batches.push(items.slice(i, i + 20));
            await writeFile(
                join(PNG_DIR, `screenshot-batch-${batches.length - 1}.json`),
                `${JSON.stringify(batches.at(-1), null, 2)}\n`,
            );
        }
        console.log(`Wrote ${batches.length} screenshot batches for ${items.length} pictos`);
        return;
    }

    if (command === "download-urls" && arg) {
        const manifest: { fileName: string; url: string }[] = JSON.parse(
            await readFile(arg, "utf8"),
        );
        const pngDir = join(import.meta.dir, "../.picto-gradient-png");
        await mkdir(pngDir, { recursive: true });
        for (const { fileName, url } of manifest) {
            const res = await fetch(url);
            if (!res.ok) throw new Error(`Failed ${fileName}: ${res.status}`);
            await writeFile(join(pngDir, `${fileName}.png`), await res.arrayBuffer());
            console.log(`Downloaded ${fileName}.png`);
        }
        return;
    }

    if (command === "generate-imports") {
        const parsed = JSON.parse(await readFile(MAPPING_PATH, "utf8"));
        const mappings: PictoMapping[] = Array.isArray(parsed) ? parsed : parsed.mappings;
        const gradientDir = join(PICTOS_DIR);
        const available = new Set(
            (await readdir(gradientDir))
                .filter((f) => f.endsWith("Gradient.webp"))
                .map((f) => f.replace(/\.webp$/, "")),
        );
        const lines: string[] = [];
        const exports: string[] = [];
        for (const m of mappings) {
            if (!available.has(m.uiKitGradientFileName)) continue;
            lines.push(
                `import ${m.uiKitGradientExportName} from "../assets/pictos/${m.uiKitGradientFileName}.webp";`,
            );
            exports.push(m.uiKitGradientExportName);
        }
        const outPath = join(import.meta.dir, "../src/constants/picto-gradients.ts");
        await writeFile(
            outPath,
            `${lines.join("\n")}\n\nexport {\n    ${exports.join(",\n    ")},\n};\n`,
        );
        console.log(`Wrote ${exports.length} gradient imports to ${outPath}`);
        return;
    }

    if (command === "generate-pictos-imports") {
        const glassFiles = (await readdir(PICTOS_DIR))
            .filter((f) => f.endsWith(".webp") && !f.endsWith("Gradient.webp"))
            .map((f) => f.replace(/\.webp$/, ""))
            .sort((a, b) => a.localeCompare(b));
        const lines = glassFiles.map(
            (fileName) =>
                `import ${fileNameToExportName(fileName)} from "../assets/pictos/${fileName}.webp";`,
        );
        const exports = glassFiles.map((fileName) => fileNameToExportName(fileName));
        const outPath = join(import.meta.dir, "../src/constants/pictos.ts");
        await writeFile(
            outPath,
            `${lines.join("\n")}\n\nexport {\n    ${exports.join(",\n    ")},\n};\n`,
        );
        console.log(`Wrote ${exports.length} glass picto imports to ${outPath}`);
        return;
    }

    if (command === "list-glass-ids") {
        const parsed = JSON.parse(await readFile(MAPPING_PATH, "utf8"));
        const mappings: PictoMapping[] = parsed.mappings ?? parsed;
        const glassFiles = new Set(
            (await readdir(PICTOS_DIR))
                .filter((f) => f.endsWith(".webp") && !f.endsWith("Gradient.webp"))
                .map((f) => f.replace(/\.webp$/, "")),
        );
        for (const m of mappings) {
            if (!glassFiles.has(m.uiKitFileName) && m.figmaLightNodeId) {
                console.log(`${m.uiKitFileName}\t${m.figmaLightNodeId}`);
            }
        }
        return;
    }

    console.error(
        `Usage: bun tools/picto-sync.ts <parse|optimize|list-vector-ids|list-glass-ids|download-urls|generate-imports|generate-pictos-imports> <path>`,
    );
    process.exit(1);
}

main().catch((err) => {
    console.error(err);
    process.exit(1);
});
