import { ClarizenApiError } from "../errors.js";
import { ENDPOINTS } from "../endpoints.js";
import { filterStandardTypeNames, sanitizeEntityDescription, } from "./filter.js";
const DEFAULT_FLAGS = ["fields", "relations"];
const DEFAULT_BATCH_SIZE = 20;
const DESCRIBE_METADATA_PATH = ENDPOINTS.find((e) => e.namespace === "metadata" && e.operation === "DescribeMetadata")?.path ?? "/V2.0/services/metadata/describeMetadata";
const LIST_ENTITIES_PATH = ENDPOINTS.find((e) => e.namespace === "metadata" && e.operation === "ListEntities")?.path ?? "/V2.0/services/metadata/listEntities";
function describeMetadataQuery(typeNames, flags) {
    const query = {
        flags,
    };
    if (typeNames !== undefined && typeNames.length > 0) {
        query.typeNames = typeNames;
    }
    return query;
}
async function listEntities(http) {
    const result = await http.request(LIST_ENTITIES_PATH, {
        method: "GET",
    });
    return result.typeNames ?? [];
}
async function describeMetadata(http, typeNames, flags) {
    const result = await http.request(DESCRIBE_METADATA_PATH, {
        method: "GET",
        query: describeMetadataQuery(typeNames, flags),
    });
    const entries = [];
    for (const desc of result.entityDescriptions ?? []) {
        const entry = sanitizeEntityDescription(desc);
        if (entry) {
            entries.push(entry);
        }
    }
    return entries;
}
function chunk(items, size) {
    const chunks = [];
    for (let i = 0; i < items.length; i += size) {
        chunks.push(items.slice(i, i + size));
    }
    return chunks;
}
function mergeEntries(batches) {
    const byType = new Map();
    for (const batch of batches) {
        for (const entry of batch) {
            byType.set(entry.typeName, entry);
        }
    }
    return [...byType.values()].sort((a, b) => a.typeName.localeCompare(b.typeName));
}
function needsBatchedFetch(error) {
    if (error instanceof ClarizenApiError) {
        return (error.errorCode === "MissingArgument" ||
            error.errorCode === "InvalidArgument");
    }
    if (error instanceof Error) {
        const msg = error.message.toLowerCase();
        return msg.includes("missing") || msg.includes("typenames");
    }
    return false;
}
async function fetchBatched(http, flags, batchSize, log) {
    log("listEntities + batched describeMetadata...");
    const rawNames = await listEntities(http);
    const typeNames = filterStandardTypeNames(rawNames);
    log(`  ${typeNames.length} standard entities (${rawNames.length} total)`);
    const batches = chunk(typeNames, batchSize);
    const batchResults = [];
    for (let i = 0; i < batches.length; i++) {
        const batch = batches[i];
        log(`  batch ${i + 1}/${batches.length} (${batch.length} types)...`);
        batchResults.push(await describeMetadata(http, batch, flags));
    }
    return mergeEntries(batchResults);
}
export async function fetchMetadataCatalog(http, options = {}) {
    const flags = options.flags ?? DEFAULT_FLAGS;
    const batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
    const log = options.onProgress ?? (() => { });
    let entries;
    try {
        log("describeMetadata (all entities)...");
        entries = await describeMetadata(http, undefined, flags);
        if (entries.length === 0) {
            entries = await fetchBatched(http, flags, batchSize, log);
        }
    }
    catch (error) {
        if (needsBatchedFetch(error)) {
            entries = await fetchBatched(http, flags, batchSize, log);
        }
        else {
            throw error;
        }
    }
    return {
        fetchedAt: new Date().toISOString(),
        entities: entries,
    };
}
//# sourceMappingURL=catalog.js.map