// engine/design-to-spec/parser.ts — Parse design description into structured screens/components (SPEC-159)
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const WEB_BREAKPOINTS = [
    { name: 'mobile', minWidth: 0, maxWidth: 767, description: 'Mobile devices (portrait)' },
    { name: 'tablet', minWidth: 768, maxWidth: 1023, description: 'Tablet devices' },
    { name: 'desktop', minWidth: 1024, description: 'Desktop and larger screens' },
];
const MOBILE_BREAKPOINTS = [
    { name: 'small', minWidth: 320, maxWidth: 374, description: 'Small mobile (320–374px)' },
    { name: 'medium', minWidth: 375, maxWidth: 413, description: 'Standard mobile (375–413px)' },
    { name: 'large', minWidth: 414, description: 'Large mobile (414px+)' },
];
const DESKTOP_BREAKPOINTS = [
    { name: 'standard', minWidth: 1024, maxWidth: 1439, description: 'Standard desktop' },
    { name: 'wide', minWidth: 1440, description: 'Wide / large desktop' },
];
const COMPONENT_KEYWORDS = {
    form: ['form', 'input', 'submit', 'field', 'label', 'signup', 'login', 'register', 'checkout'],
    button: ['button', 'btn', 'cta', 'action', 'click', 'tap', 'link'],
    input: ['input', 'textbox', 'textarea', 'search', 'field', 'entry'],
    list: ['list', 'table', 'grid', 'feed', 'items', 'rows', 'columns'],
    modal: ['modal', 'dialog', 'popup', 'overlay', 'drawer', 'sheet'],
    nav: ['nav', 'navigation', 'menu', 'sidebar', 'header', 'footer', 'tab', 'breadcrumb'],
    card: ['card', 'tile', 'panel', 'widget', 'item', 'product'],
    text: ['text', 'paragraph', 'heading', 'title', 'label', 'copy', 'content'],
    image: ['image', 'img', 'photo', 'icon', 'avatar', 'thumbnail', 'banner'],
    container: ['container', 'section', 'wrapper', 'layout', 'page', 'screen', 'view'],
    other: [],
};
const INTERACTION_PATTERNS = [
    { trigger: /hover/i, name: 'hover', visualChange: 'Highlight or shadow change on hover' },
    { trigger: /click|tap/i, name: 'active', visualChange: 'Pressed state visual feedback' },
    { trigger: /focus/i, name: 'focus', visualChange: 'Focus ring visible around element' },
    { trigger: /disabled/i, name: 'disabled', visualChange: 'Reduced opacity, cursor: not-allowed' },
    {
        trigger: /loading|spinner/i,
        name: 'loading',
        visualChange: 'Loading spinner or skeleton shown',
    },
    { trigger: /error|invalid/i, name: 'error', visualChange: 'Red border, error message displayed' },
    { trigger: /success|complete/i, name: 'success', visualChange: 'Success indicator shown' },
];
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Infer component type from a name/description string */
function inferComponentType(text) {
    const lower = text.toLowerCase();
    for (const [type, keywords] of Object.entries(COMPONENT_KEYWORDS)) {
        if (type === 'other') {
            continue;
        }
        if (keywords.some((kw) => lower.includes(kw))) {
            return type;
        }
    }
    return 'other';
}
/** Extract interaction states from a block of text */
function extractInteractionStates(text) {
    const states = [];
    for (const pattern of INTERACTION_PATTERNS) {
        if (pattern.trigger.test(text)) {
            states.push({
                name: pattern.name,
                trigger: `User ${pattern.name}s the element`,
                visualChange: pattern.visualChange,
            });
        }
    }
    // Always include default state
    if (states.length === 0) {
        states.push({ name: 'default', trigger: 'Element rendered', visualChange: 'Normal display' });
    }
    return states;
}
/** Build a minimal prop from a keyword */
function buildProp(name, type, required, description) {
    return { name, type, required, description };
}
/** Generate basic props based on component type */
function generatePropsForType(componentType) {
    switch (componentType) {
        case 'button':
            return [
                buildProp('label', 'string', true, 'Button text label'),
                buildProp('onClick', '() => void', true, 'Click handler'),
                buildProp('disabled', 'boolean', false, 'Disables the button when true'),
                buildProp('variant', "'primary' | 'secondary' | 'ghost'", false, 'Visual variant'),
            ];
        case 'input':
            return [
                buildProp('value', 'string', true, 'Current input value'),
                buildProp('onChange', '(v: string) => void', true, 'Change handler'),
                buildProp('placeholder', 'string', false, 'Placeholder text'),
                buildProp('error', 'string', false, 'Error message to display'),
            ];
        case 'list':
            return [
                buildProp('items', 'unknown[]', true, 'Array of items to render'),
                buildProp('loading', 'boolean', false, 'Shows loading state when true'),
                buildProp('emptyMessage', 'string', false, 'Message when list is empty'),
            ];
        case 'modal':
            return [
                buildProp('isOpen', 'boolean', true, 'Controls modal visibility'),
                buildProp('onClose', '() => void', true, 'Close handler'),
                buildProp('title', 'string', false, 'Modal title'),
            ];
        case 'card':
            return [
                buildProp('title', 'string', true, 'Card title'),
                buildProp('description', 'string', false, 'Card description text'),
                buildProp('onClick', '() => void', false, 'Card click handler'),
            ];
        case 'container':
        case 'form':
        case 'nav':
        case 'text':
        case 'image':
        case 'other':
            // 'node' is a framework-agnostic placeholder — actual type depends on the project's UI framework
            // (e.g. React.ReactNode, Vue's VNode, Angular's TemplateRef, plain HTMLElement, etc.)
            return [buildProp('children', 'node', false, 'Child content')];
    }
}
/** Slugify a name to a valid ID */
function toId(name) {
    return name
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, '-')
        .replace(/(^-|-$)/g, '');
}
// ---------------------------------------------------------------------------
// Screen extraction
// ---------------------------------------------------------------------------
/** Split description text into screen blocks by common screen delimiters */
function splitIntoScreenBlocks(description, screenNames) {
    if (screenNames.length > 0) {
        // Use provided screen names as anchors
        const blocks = [];
        let remaining = description;
        for (const name of screenNames) {
            const idx = remaining.toLowerCase().indexOf(name.toLowerCase());
            if (idx !== -1) {
                const before = remaining.slice(0, idx).trim();
                if (before) {
                    blocks.push(before);
                }
                remaining = remaining.slice(idx);
            }
        }
        if (remaining.trim()) {
            blocks.push(remaining.trim());
        }
        return blocks.length > 1 ? blocks : [description];
    }
    // Auto-split on "Screen:", "Page:", "View:", or numbered sections
    const screenPattern = /(?=(?:screen|page|view|section)\s*[:–-]?\s*\w)/gi;
    const parts = description.split(screenPattern).filter(Boolean);
    return parts.length > 1 ? parts : [description];
}
/** Extract component descriptions from a text block */
function extractComponentsFromText(text) {
    const components = [];
    // Match bullet-style component descriptions
    const bulletPattern = /[-•*]\s+(.+?)(?=\n[-•*]|\n\n|$)/gs;
    let match;
    let index = 0;
    while ((match = bulletPattern.exec(text)) !== null) {
        const raw = (match[1] ?? '').trim();
        const type = inferComponentType(raw);
        const id = `comp-${toId(raw.slice(0, 20))}-${index}`;
        components.push({
            id,
            name: raw.slice(0, 50),
            type,
            description: raw,
            props: generatePropsForType(type),
            interactionStates: extractInteractionStates(raw),
            a11yRequirements: [],
            children: [],
        });
        index++;
    }
    // If no bullets found, treat whole block as single component
    if (components.length === 0 && text.trim().length > 0) {
        const type = inferComponentType(text);
        components.push({
            id: `comp-main-${index}`,
            name: 'Main Content',
            type,
            description: text.slice(0, 200),
            props: generatePropsForType(type),
            interactionStates: extractInteractionStates(text),
            a11yRequirements: [],
            children: [],
        });
    }
    return components;
}
/** Extract data bindings from text (looks for "bound to", "fetches", "loads", "displays") */
function extractDataBindings(text, components) {
    const bindings = [];
    const bindingPattern = /(?:bound to|fetches|loads|displays|shows)\s+([\w\s]+?)(?:\.|,|from|via|$)/gi;
    let match;
    while ((match = bindingPattern.exec(text)) !== null) {
        const dataSource = (match[1] ?? '').trim();
        if (!dataSource) {
            continue;
        }
        // Try to match the binding to the component whose description mentions the data source keyword;
        // fall back to the first component if no specific match is found.
        const keyword = dataSource.toLowerCase();
        const targetComp = components.find((c) => c.description.toLowerCase().includes(keyword)) ?? components[0];
        if (targetComp) {
            bindings.push({
                componentId: targetComp.id,
                dataSource,
                fieldMapping: `${toId(dataSource)} → ${targetComp.name}`,
                loadingState: 'Loading indicator shown while fetching',
                errorState: 'Error message displayed on fetch failure',
            });
        }
    }
    return bindings;
}
/** Parse a single screen block into a DesignScreen */
function parseScreenBlock(block, index, screenNames, platform) {
    const nameMatch = /^(?:screen|page|view|section)?\s*[:–-]?\s*([^\n.]+)/i.exec(block);
    const screenName = screenNames[index] ?? nameMatch?.[1]?.trim() ?? `Screen ${index + 1}`;
    const id = `screen-${toId(screenName)}-${index}`;
    const breakpoints = platform === 'web'
        ? WEB_BREAKPOINTS
        : platform === 'mobile'
            ? MOBILE_BREAKPOINTS
            : DESKTOP_BREAKPOINTS;
    const components = extractComponentsFromText(block);
    const dataBindings = extractDataBindings(block, components);
    return {
        id,
        name: screenName,
        description: block.slice(0, 300).trim(),
        components,
        breakpoints,
        acceptanceCriteria: [],
        a11yRequirements: [],
        dataBindings,
    };
}
/** Extract design tokens from description (colors, fonts, spacing hints) */
function extractDesignTokens(description) {
    const tokens = [];
    const colorPattern = /#[0-9a-fA-F]{3,6}|rgb\([^)]+\)|hsl\([^)]+\)/g;
    let match;
    let idx = 0;
    while ((match = colorPattern.exec(description)) !== null) {
        tokens.push({
            name: `color-${idx++}`,
            category: 'color',
            value: match[0],
        });
    }
    return tokens;
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Parses a design description string into a structured ParsedDesign representation.
 * Extracts screens, components, breakpoints, and design tokens without any I/O.
 */
export function parseDesignDescription(description, screenNames, platform) {
    const trimmed = description.trim();
    const titleMatch = /^([^\n.!?]{5,80})/.exec(trimmed);
    const title = titleMatch?.[1]?.trim() ?? 'Untitled Design';
    const blocks = splitIntoScreenBlocks(trimmed, screenNames);
    const screens = blocks.map((block, i) => parseScreenBlock(block, i, screenNames, platform));
    const tokens = extractDesignTokens(trimmed);
    return {
        title,
        platform,
        screens,
        globalComponents: [],
        designTokens: tokens,
    };
}
//# sourceMappingURL=parser.js.map