import { z } from 'zod';
import { defaultAtomicBlockContracts, defaultBlockContracts, } from './contract.js';
import { validateContentClassName, validateComponentClassName } from './tailwind-validator.js';
const linkFields = {
    id: z.string().min(1),
    label: z.string().min(1),
    href: z.string().min(1),
};
export const linkSchema = z.looseObject(linkFields);
export const imageSchema = z.looseObject({
    src: z.string().min(1),
    alt: z.string().min(1),
});
export const optionalImageSchema = z.looseObject({
    src: z.string().min(1),
    alt: z.string().optional(),
});
export const siteConfigSchema = z.looseObject({
    id: z.string().min(1),
    $type: z.literal('site'),
    status: z.string().min(1),
    title: z.string().min(1),
    domain: z.string().min(1),
    defaultLocale: z.string().min(1),
    brandHref: z.string().optional(),
    brandLogo: optionalImageSchema.optional(),
    navigation: z.array(linkSchema).optional(),
    utilities: z.array(z.looseObject({
        ...linkFields,
        variant: z.enum(['primary', 'secondary', 'tertiary', 'outline', 'ghost']).optional(),
    })).optional(),
    promo: z.record(z.string(), z.unknown()).optional(),
    footer: z.looseObject({
        columns: z.array(z.looseObject({
            id: z.string().min(1),
            title: z.string().optional(),
            groups: z.array(z.looseObject({
                id: z.string().min(1),
                title: z.string(),
                links: z.array(linkSchema),
            })).optional(),
            links: z.array(linkSchema).optional(),
        })).optional(),
        social: z.array(z.looseObject({
            ...linkFields,
            icon: z.string().optional(),
        })).optional(),
        legalLinks: z.array(linkSchema).optional(),
        copyright: z.string().optional(),
    }).optional(),
    seo: z.looseObject({
        title: z.string().optional(),
        description: z.string().optional(),
        siteName: z.string().optional(),
    }).optional(),
});
function withOptional(schema, field) {
    return field.optional ? schema.optional() : schema;
}
export function createContentSchemas(contracts = {}) {
    const blockContracts = contracts.blockContracts ?? defaultBlockContracts;
    const atomicBlockContracts = contracts.atomicBlockContracts ?? defaultAtomicBlockContracts;
    let atomicBlockSchema;
    function schemaFromContractField(field) {
        switch (field.kind) {
            case 'atomicBlocks':
                return withOptional(z.array(atomicBlockSchema), field);
            case 'array':
                return withOptional(z.array(schemaFromContractField(field.items)), field);
            case 'boolean':
                return withOptional(z.boolean(), field);
            case 'className': {
                const schema = z.string().check((ctx) => {
                    const result = validateContentClassName(ctx.value);
                    if (!result.valid) {
                        ctx.issues.push({
                            code: 'custom',
                            message: `Invalid Tailwind classes: ${result.invalidClasses.join(', ')}. Use standard utilities only; arbitrary bracket values are not allowed in content.`,
                            input: ctx.value,
                        });
                    }
                });
                return withOptional(schema, field);
            }
            case 'componentClassName': {
                const schema = z.string().check((ctx) => {
                    const result = validateComponentClassName(ctx.value);
                    if (!result.valid) {
                        ctx.issues.push({
                            code: 'custom',
                            message: `Invalid Tailwind classes: ${result.invalidClasses.join(', ')}. Raw dimension/color bracket values (for example [12px] or [#fff]) are not allowed.`,
                            input: ctx.value,
                        });
                    }
                });
                return withOptional(schema, field);
            }
            case 'enum':
                return withOptional(z.enum(field.values), field);
            case 'image':
                return withOptional(imageSchema, field);
            case 'object':
                return withOptional(schemaFromContractFields(field.fields), field);
            case 'richText':
                return withOptional(z.string(), field);
            case 'string': {
                const schema = typeof field.minLength === 'number'
                    ? z.string().min(field.minLength)
                    : z.string();
                return withOptional(schema, field);
            }
        }
    }
    function schemaFromContractFields(fields) {
        return z.looseObject(Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, schemaFromContractField(field)])));
    }
    atomicBlockSchema = discriminatedContractUnion('atomic block', atomicBlockContracts, schemaFromContractFields);
    const pageBlockSchema = discriminatedContractUnion('page block', blockContracts, schemaFromContractFields);
    const pageSchema = z.looseObject({
        id: z.string().min(1),
        $type: z.literal('page'),
        status: z.string().min(1),
        layout: z.string().min(1),
        renderMode: z.string().min(1),
        metadata: z.looseObject({
            title: z.string().optional(),
            description: z.string().optional(),
            canonical: z.string().optional(),
        }),
        regions: z.record(z.string(), z.array(pageBlockSchema)),
    });
    return {
        siteConfigSchema,
        atomicBlockSchema,
        pageBlockSchema,
        pageSchema,
        schemaFromContractField,
        schemaFromContractFields,
        parseKernelSiteConfig(value, source = 'site.json') {
            return parseWithSchema(siteConfigSchema, value, source);
        },
        parseKernelPage(value, source = '_index.json') {
            return parseWithSchema(pageSchema, value, source);
        },
    };
}
function discriminatedContractUnion(label, contracts, schemaFromContractFields) {
    if (contracts.length === 0) {
        throw new Error(`At least one ${label} contract is required`);
    }
    const schemas = contracts.map((block) => z.looseObject({
        id: z.string().min(1),
        component: z.literal(block.component),
        props: schemaFromContractFields(block.props),
    }));
    if (schemas.length === 1) {
        return schemas[0];
    }
    return z.discriminatedUnion('component', schemas);
}
export function formatZodError(source, error) {
    const issues = error.issues
        .map((issue) => {
        const path = issue.path.length ? issue.path.join('.') : '<root>';
        return `- ${path}: ${issue.message}`;
    })
        .join('\n');
    return `Invalid Gradial content in ${source}:\n${issues}`;
}
export function parseWithSchema(schema, value, source) {
    const parsed = schema.safeParse(value);
    if (!parsed.success) {
        throw new Error(formatZodError(source, parsed.error));
    }
    return parsed.data;
}
const defaultContentSchemas = createContentSchemas();
export const atomicBlockSchema = defaultContentSchemas.atomicBlockSchema;
export const pageBlockSchema = defaultContentSchemas.pageBlockSchema;
export const pageSchema = defaultContentSchemas.pageSchema;
export const schemaFromContractField = defaultContentSchemas.schemaFromContractField;
export const schemaFromContractFields = defaultContentSchemas.schemaFromContractFields;
export const parseKernelSiteConfig = defaultContentSchemas.parseKernelSiteConfig;
export const parseKernelPage = defaultContentSchemas.parseKernelPage;
