const HEADER = [
    '/* eslint-disable */',
    '/* prettier-ignore */',
    '// ──────────────────────────────────────────────────────────────────────',
    '// Auto-generated by @actuate-media/cms-core — DO NOT EDIT',
    '// Re-run codegen after changing actuate.config.ts',
    '// ──────────────────────────────────────────────────────────────────────',
    '',
].join('\n');
// ─── Public API ──────────────────────────────────────────────────────
export function generateTypes(config) {
    const out = [HEADER];
    emitSection(out, 'Base Types');
    emitDocumentStatus(out);
    emitDocumentBase(out);
    emitFindResult(out);
    emitFindOptions(out);
    const collectionSlugs = Object.keys(config.collections);
    const collectionTypeMap = {};
    emitSection(out, 'Collections');
    for (const slug of collectionSlugs) {
        const typeName = `${pascalCase(slug)}Document`;
        collectionTypeMap[slug] = typeName;
        emitCollectionTypes(out, slug, typeName, config.collections[slug]);
    }
    emitCollectionsMap(out, collectionSlugs, collectionTypeMap);
    emitCollectionSlugType(out, collectionSlugs);
    emitCollectionDocumentUtility(out);
    const globalSlugs = config.globals ? Object.keys(config.globals) : [];
    const globalTypeMap = {};
    if (config.globals) {
        if (globalSlugs.length > 0) {
            emitSection(out, 'Globals');
        }
        for (const slug of globalSlugs) {
            const typeName = `${pascalCase(slug)}Global`;
            globalTypeMap[slug] = typeName;
            emitGlobalTypes(out, typeName, config.globals[slug]);
        }
        if (globalSlugs.length > 0) {
            emitGlobalsMap(out, globalSlugs, globalTypeMap);
            emitGlobalSlugType(out, globalSlugs);
        }
    }
    emitSection(out, 'Client');
    emitClient(out, globalSlugs.length > 0);
    emitSection(out, 'Utilities');
    emitExtractBlock(out);
    emitSchema(out, globalSlugs.length > 0);
    return out.join('\n');
}
export async function runCodegen() {
    const { writeFile } = await import('node:fs/promises');
    const { resolve } = await import('node:path');
    const configPath = resolve(process.cwd(), 'actuate.config.ts');
    const mod = await import(configPath);
    const config = mod.default ?? mod;
    const types = generateTypes(config);
    const outPath = resolve(process.cwd(), 'actuate.generated.d.ts');
    await writeFile(outPath, types, 'utf-8');
    console.log(`Types written to ${outPath}`);
}
// ─── Section dividers ────────────────────────────────────────────────
function emitSection(out, title) {
    const pad = 68 - title.length;
    out.push(`// ─── ${title} ${'─'.repeat(Math.max(pad, 3))}`);
    out.push('');
}
// ─── Document base ───────────────────────────────────────────────────
function emitDocumentStatus(out) {
    out.push('export type DocumentStatus = "DRAFT" | "PUBLISHED" | "ARCHIVED" | "SCHEDULED";');
    out.push('');
}
function emitDocumentBase(out) {
    out.push('export interface Document {');
    out.push('  id: string;');
    out.push('  status: DocumentStatus;');
    out.push('  /** ISO 8601 date-time */');
    out.push('  createdAt: string;');
    out.push('  /** ISO 8601 date-time */');
    out.push('  updatedAt: string;');
    out.push('  /** ISO 8601 date-time, null when not yet published */');
    out.push('  publishedAt: string | null;');
    out.push('  /** ISO 8601 date-time, null when not soft-deleted */');
    out.push('  deletedAt: string | null;');
    out.push('  collection: string;');
    out.push('  locale: string | null;');
    out.push('  folderId: string | null;');
    out.push('}');
    out.push('');
}
function emitFindResult(out) {
    out.push('export interface FindResult<T> {');
    out.push('  docs: T[];');
    out.push('  totalDocs: number;');
    out.push('  totalPages: number;');
    out.push('  page: number;');
    out.push('  hasNextPage: boolean;');
    out.push('  hasPrevPage: boolean;');
    out.push('}');
    out.push('');
}
function emitFindOptions(out) {
    out.push('export interface FindOptions {');
    out.push('  where?: Record<string, unknown>;');
    out.push('  sort?: string;');
    out.push('  limit?: number;');
    out.push('  page?: number;');
    out.push('  locale?: string;');
    out.push('  status?: DocumentStatus | DocumentStatus[];');
    out.push('  includeDeleted?: boolean;');
    out.push('}');
    out.push('');
}
// ─── Collections ─────────────────────────────────────────────────────
function emitCollectionTypes(out, slug, typeName, collection) {
    emitAuxTypes(out, typeName, collection.fields);
    const label = collection.labels.singular;
    out.push(`/** ${label} document — collection \`"${slug}"\` */`);
    out.push(`export interface ${typeName} extends Document {`);
    out.push(`  collection: "${slug}";`);
    for (const [fieldName, field] of Object.entries(collection.fields)) {
        const jsdoc = buildFieldJSDoc(field);
        if (jsdoc)
            out.push(jsdoc);
        const optional = field.required ? '' : '?';
        out.push(`  ${fieldName}${optional}: ${resolveFieldType(typeName, fieldName, field)};`);
    }
    out.push('}');
    out.push('');
    out.push(`export type ${typeName}CreateInput = Omit<${typeName}, keyof Document>;`);
    out.push('');
    out.push(`export type ${typeName}UpdateInput = Partial<${typeName}CreateInput>;`);
    out.push('');
}
function emitCollectionsMap(out, slugs, typeMap) {
    out.push('export interface ActuateCollections {');
    for (const slug of slugs) {
        out.push(`  ${slug}: ${typeMap[slug]};`);
    }
    out.push('}');
    out.push('');
}
function emitCollectionSlugType(out, slugs) {
    if (slugs.length === 0) {
        out.push('export type ActuateCollectionSlug = never;');
    }
    else {
        out.push(`export type ActuateCollectionSlug = ${slugs.map((s) => `"${s}"`).join(' | ')};`);
    }
    out.push('');
}
function emitCollectionDocumentUtility(out) {
    out.push('export type CollectionDocument<K extends ActuateCollectionSlug> = ActuateCollections[K];');
    out.push('');
}
// ─── Globals ─────────────────────────────────────────────────────────
function emitGlobalTypes(out, typeName, global) {
    emitAuxTypes(out, typeName, global.fields);
    out.push(`/** ${global.label} (global) */`);
    out.push(`export interface ${typeName} {`);
    for (const [fieldName, field] of Object.entries(global.fields)) {
        const jsdoc = buildFieldJSDoc(field);
        if (jsdoc)
            out.push(jsdoc);
        const optional = field.required ? '' : '?';
        out.push(`  ${fieldName}${optional}: ${resolveFieldType(typeName, fieldName, field)};`);
    }
    out.push('}');
    out.push('');
}
function emitGlobalsMap(out, slugs, typeMap) {
    out.push('export interface ActuateGlobals {');
    for (const slug of slugs) {
        out.push(`  ${slug}: ${typeMap[slug]};`);
    }
    out.push('}');
    out.push('');
}
function emitGlobalSlugType(out, slugs) {
    if (slugs.length === 0) {
        out.push('export type ActuateGlobalSlug = never;');
    }
    else {
        out.push(`export type ActuateGlobalSlug = ${slugs.map((s) => `"${s}"`).join(' | ')};`);
    }
    out.push('');
}
// ─── Auxiliary types (arrays, blocks) ────────────────────────────────
function emitAuxTypes(out, parentTypeName, fields) {
    for (const [fieldName, field] of Object.entries(fields)) {
        if (field.type === 'array') {
            const subTypeName = `${parentTypeName}_${pascalCase(fieldName)}Item`;
            emitAuxTypes(out, subTypeName, field.fields);
            out.push(`export interface ${subTypeName} {`);
            for (const [subName, subField] of Object.entries(field.fields)) {
                const jsdoc = buildFieldJSDoc(subField);
                if (jsdoc)
                    out.push(jsdoc);
                const optional = subField.required ? '' : '?';
                out.push(`  ${subName}${optional}: ${resolveFieldType(subTypeName, subName, subField)};`);
            }
            out.push('}');
            out.push('');
        }
        else if (field.type === 'blocks') {
            const blockTypeNames = [];
            for (const block of field.blocks) {
                const blockTypeName = `${parentTypeName}_${pascalCase(block.slug)}Block`;
                blockTypeNames.push(blockTypeName);
                emitAuxTypes(out, blockTypeName, block.fields);
                out.push(`export interface ${blockTypeName} {`);
                out.push(`  /** Discriminator — use to narrow the block union. */`);
                out.push(`  blockType: "${block.slug}";`);
                for (const [bFieldName, bField] of Object.entries(block.fields)) {
                    const jsdoc = buildFieldJSDoc(bField);
                    if (jsdoc)
                        out.push(jsdoc);
                    const optional = bField.required ? '' : '?';
                    out.push(`  ${bFieldName}${optional}: ${resolveFieldType(blockTypeName, bFieldName, bField)};`);
                }
                out.push('}');
                out.push('');
            }
            const unionName = `${parentTypeName}_${pascalCase(fieldName)}Blocks`;
            if (blockTypeNames.length > 0) {
                out.push(`/** Discriminated union on \`blockType\` — narrow with \`ExtractBlock\`. */`);
                out.push(`export type ${unionName} = ${blockTypeNames.join(' | ')};`);
            }
            else {
                out.push(`export type ${unionName} = Record<string, unknown>;`);
            }
            out.push('');
        }
    }
}
// ─── Field type resolution ───────────────────────────────────────────
function resolveFieldType(parentTypeName, fieldName, field) {
    switch (field.type) {
        case 'text':
        case 'slug':
        case 'color':
        case 'url':
        case 'email':
            return 'string';
        case 'richText':
            return 'Record<string, unknown>';
        case 'number':
            return 'number';
        case 'boolean':
            return 'boolean';
        case 'date':
            return 'string';
        case 'select': {
            const union = field.options.length > 0 ? field.options.map((o) => `"${o.value}"`).join(' | ') : 'string';
            return field.multiple ? `(${union})[]` : union;
        }
        case 'relationship':
            return field.hasMany ? 'string[]' : 'string';
        case 'media':
            return 'string';
        case 'json':
            return 'Record<string, unknown>';
        case 'array':
            return `${parentTypeName}_${pascalCase(fieldName)}Item[]`;
        case 'blocks':
            return `${parentTypeName}_${pascalCase(fieldName)}Blocks[]`;
        default:
            return 'unknown';
    }
}
// ─── JSDoc helpers ───────────────────────────────────────────────────
function buildFieldJSDoc(field) {
    const parts = [];
    if (field.label) {
        parts.push(field.label);
    }
    if (field.type === 'relationship') {
        const targets = Array.isArray(field.relationTo)
            ? field.relationTo.map((r) => `${pascalCase(r)}Document`).join(' | ')
            : `${pascalCase(field.relationTo)}Document`;
        parts.push(`@relation ${targets}`);
    }
    if (field.type === 'date' && 'includeTime' in field && field.includeTime) {
        parts.push('@format ISO 8601 date-time');
    }
    else if (field.type === 'date') {
        parts.push('@format ISO 8601 date');
    }
    if (parts.length === 0)
        return null;
    if (parts.length === 1 && !parts[0].startsWith('@')) {
        return `  /** ${parts[0]} */`;
    }
    const lines = ['  /**'];
    for (const part of parts) {
        lines.push(`   * ${part}`);
    }
    lines.push('   */');
    return lines.join('\n');
}
// ─── Client ─────────────────────────────────────────────────────────
function emitClient(out, hasGlobals) {
    out.push('export interface ActuateClient {');
    out.push('  find<K extends keyof ActuateCollections>(');
    out.push('    collection: K,');
    out.push('    options?: FindOptions,');
    out.push('  ): Promise<FindResult<ActuateCollections[K]>>;');
    out.push('');
    out.push('  findOne<K extends keyof ActuateCollections>(');
    out.push('    collection: K,');
    out.push('    id: string,');
    out.push('  ): Promise<ActuateCollections[K] | null>;');
    out.push('');
    out.push('  create<K extends keyof ActuateCollections>(');
    out.push('    collection: K,');
    out.push('    data: Omit<ActuateCollections[K], keyof Document>,');
    out.push('  ): Promise<ActuateCollections[K]>;');
    out.push('');
    out.push('  update<K extends keyof ActuateCollections>(');
    out.push('    collection: K,');
    out.push('    id: string,');
    out.push('    data: Partial<Omit<ActuateCollections[K], keyof Document>>,');
    out.push('  ): Promise<ActuateCollections[K]>;');
    out.push('');
    out.push('  delete<K extends keyof ActuateCollections>(');
    out.push('    collection: K,');
    out.push('    id: string,');
    out.push('  ): Promise<void>;');
    if (hasGlobals) {
        out.push('');
        out.push('  getGlobal<K extends keyof ActuateGlobals>(');
        out.push('    slug: K,');
        out.push('  ): Promise<ActuateGlobals[K]>;');
        out.push('');
        out.push('  updateGlobal<K extends keyof ActuateGlobals>(');
        out.push('    slug: K,');
        out.push('    data: Partial<ActuateGlobals[K]>,');
        out.push('  ): Promise<ActuateGlobals[K]>;');
    }
    out.push('}');
    out.push('');
}
// ─── Utility types ──────────────────────────────────────────────────
function emitExtractBlock(out) {
    out.push('/** Narrow a block discriminated union to a single block type by its `blockType` slug. */');
    out.push('export type ExtractBlock<Union, Slug extends string> = Union extends { blockType: Slug } ? Union : never;');
    out.push('');
}
function emitSchema(out, hasGlobals) {
    out.push('export interface ActuateSchema {');
    out.push('  collections: ActuateCollections;');
    if (hasGlobals) {
        out.push('  globals: ActuateGlobals;');
    }
    out.push('}');
    out.push('');
}
// ─── String utilities ───────────────────────────────────────────────
function pascalCase(str) {
    return str
        .split(/[-_\s]+/)
        .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
        .join('');
}
//# sourceMappingURL=index.js.map