/**
 * @module chevrotain-parser/trigger-cancel-parser
 *
 * Shared Chevrotain parser for @trigger, @cancelOn, @retries, @timeout, @throttle annotations.
 */
import { CstParser } from 'chevrotain';
import { JSDocLexer, TriggerTag, CancelOnTag, RetriesTag, TimeoutTag, ThrottleTag, EventEq, CronEq, MatchEq, TimeoutEq, LimitEq, PeriodEq, StringLiteral, Integer, allTokens, } from './tokens.js';
// =============================================================================
// Helpers
// =============================================================================
function stripQuotes(s) {
    if (s.startsWith('"') && s.endsWith('"')) {
        return s.slice(1, -1);
    }
    return s;
}
const CRON_REGEX = /^(\*|[0-9,\-\/]+)\s+(\*|[0-9,\-\/]+)\s+(\*|[0-9,\-\/]+)\s+(\*|[0-9,\-\/]+)\s+(\*|[0-9,\-\/]+)$/;
// =============================================================================
// Parser Definition
// =============================================================================
class TriggerCancelParser extends CstParser {
    constructor() {
        super(allTokens);
        this.performSelfAnalysis();
    }
    // @trigger event="agent/request" cron="0 9 * * *"
    triggerLine = this.RULE('triggerLine', () => {
        this.CONSUME(TriggerTag);
        this.MANY(() => {
            this.OR([
                { ALT: () => {
                        this.CONSUME(EventEq);
                        this.CONSUME(StringLiteral, { LABEL: 'eventValue' });
                    } },
                { ALT: () => {
                        this.CONSUME(CronEq);
                        this.CONSUME2(StringLiteral, { LABEL: 'cronValue' });
                    } },
            ]);
        });
    });
    // @cancelOn event="app/user.deleted" match="data.userId" timeout="1h"
    cancelOnLine = this.RULE('cancelOnLine', () => {
        this.CONSUME(CancelOnTag);
        this.CONSUME(EventEq);
        this.CONSUME(StringLiteral, { LABEL: 'eventValue' });
        this.OPTION(() => {
            this.CONSUME(MatchEq);
            this.CONSUME2(StringLiteral, { LABEL: 'matchValue' });
        });
        this.OPTION2(() => {
            this.CONSUME(TimeoutEq);
            this.CONSUME3(StringLiteral, { LABEL: 'timeoutValue' });
        });
    });
    // @retries 5
    retriesLine = this.RULE('retriesLine', () => {
        this.CONSUME(RetriesTag);
        this.CONSUME(Integer, { LABEL: 'retriesValue' });
    });
    // @timeout "30m"
    timeoutLine = this.RULE('timeoutLine', () => {
        this.CONSUME(TimeoutTag);
        this.CONSUME(StringLiteral, { LABEL: 'timeoutValue' });
    });
    // @throttle limit=3 period="1m"
    throttleLine = this.RULE('throttleLine', () => {
        this.CONSUME(ThrottleTag);
        this.CONSUME(LimitEq);
        this.CONSUME(Integer, { LABEL: 'limitValue' });
        this.OPTION(() => {
            this.CONSUME(PeriodEq);
            this.CONSUME(StringLiteral, { LABEL: 'periodValue' });
        });
    });
}
// =============================================================================
// Parser Instance (singleton)
// =============================================================================
const parserInstance = new TriggerCancelParser();
// =============================================================================
// CST Visitor
// =============================================================================
const BaseVisitor = parserInstance.getBaseCstVisitorConstructor();
class TriggerCancelVisitor extends BaseVisitor {
    constructor() {
        super();
        this.validateVisitor();
    }
    triggerLine(ctx) {
        const result = {};
        if (ctx.eventValue?.[0]) {
            result.event = stripQuotes(ctx.eventValue[0].image);
        }
        if (ctx.cronValue?.[0]) {
            result.cron = stripQuotes(ctx.cronValue[0].image);
        }
        return result;
    }
    cancelOnLine(ctx) {
        const result = {
            event: stripQuotes(ctx.eventValue[0].image),
        };
        if (ctx.matchValue?.[0]) {
            result.match = stripQuotes(ctx.matchValue[0].image);
        }
        if (ctx.timeoutValue?.[0]) {
            result.timeout = stripQuotes(ctx.timeoutValue[0].image);
        }
        return result;
    }
    retriesLine(ctx) {
        return { retries: parseInt(ctx.retriesValue[0].image, 10) };
    }
    timeoutLine(ctx) {
        return { timeout: stripQuotes(ctx.timeoutValue[0].image) };
    }
    throttleLine(ctx) {
        const result = {
            limit: parseInt(ctx.limitValue[0].image, 10),
        };
        if (ctx.periodValue?.[0]) {
            result.period = stripQuotes(ctx.periodValue[0].image);
        }
        return result;
    }
}
const visitorInstance = new TriggerCancelVisitor();
// =============================================================================
// Public API
// =============================================================================
/**
 * Parse a @trigger line and return structured result.
 * Returns null if the line is not a trigger declaration.
 */
export function parseTriggerLine(input, warnings) {
    const lexResult = JSDocLexer.tokenize(input);
    if (lexResult.errors.length > 0) {
        return null;
    }
    if (lexResult.tokens.length === 0) {
        return null;
    }
    const firstToken = lexResult.tokens[0];
    if (firstToken.tokenType !== TriggerTag) {
        return null;
    }
    parserInstance.input = lexResult.tokens;
    const cst = parserInstance.triggerLine();
    if (parserInstance.errors.length > 0) {
        // Don't warn here — return null so domain-specific handlers (e.g. CI/CD)
        // get a chance to parse the trigger. The caller can warn if nothing handles it.
        return null;
    }
    const result = visitorInstance.visit(cst);
    // Empty result means the parser consumed @trigger but found no event=/cron= assignments.
    // Return null so the caller can delegate to domain-specific handlers (e.g. CI/CD triggers
    // like @trigger push, @trigger pull_request, etc.)
    if (!result.event && !result.cron) {
        return null;
    }
    // Validate cron expression
    if (result.cron && !CRON_REGEX.test(result.cron)) {
        warnings.push(`Invalid cron expression: "${result.cron}". Expected 5 fields (minute hour day month weekday).`);
    }
    return result;
}
/**
 * Parse a @cancelOn line and return structured result.
 * Returns null if the line is not a cancelOn declaration.
 */
export function parseCancelOnLine(input, warnings) {
    const lexResult = JSDocLexer.tokenize(input);
    if (lexResult.errors.length > 0) {
        return null;
    }
    if (lexResult.tokens.length === 0) {
        return null;
    }
    const firstToken = lexResult.tokens[0];
    if (firstToken.tokenType !== CancelOnTag) {
        return null;
    }
    parserInstance.input = lexResult.tokens;
    const cst = parserInstance.cancelOnLine();
    if (parserInstance.errors.length > 0) {
        const firstError = parserInstance.errors[0];
        const truncatedInput = input.length > 60 ? input.substring(0, 60) + '...' : input;
        warnings.push(`Failed to parse cancelOn line: "${truncatedInput}"\n` +
            `  Error: ${firstError.message}\n` +
            `  Expected format: @cancelOn event="name" match="field" timeout="duration"`);
        return null;
    }
    return visitorInstance.visit(cst);
}
/**
 * Parse a @retries line and return structured result.
 * Returns null if the line is not a retries declaration.
 */
export function parseRetriesLine(input, warnings) {
    const lexResult = JSDocLexer.tokenize(input);
    if (lexResult.errors.length > 0) {
        return null;
    }
    if (lexResult.tokens.length === 0) {
        return null;
    }
    const firstToken = lexResult.tokens[0];
    if (firstToken.tokenType !== RetriesTag) {
        return null;
    }
    parserInstance.input = lexResult.tokens;
    const cst = parserInstance.retriesLine();
    if (parserInstance.errors.length > 0) {
        const firstError = parserInstance.errors[0];
        const truncatedInput = input.length > 60 ? input.substring(0, 60) + '...' : input;
        warnings.push(`Failed to parse retries line: "${truncatedInput}"\n` +
            `  Error: ${firstError.message}\n` +
            `  Expected format: @retries <integer>`);
        return null;
    }
    const result = visitorInstance.visit(cst);
    // Validate non-negative
    if (result.retries < 0) {
        warnings.push(`Invalid @retries value: ${result.retries}. Expected non-negative integer.`);
    }
    return result;
}
/**
 * Parse a @timeout line and return structured result.
 * Returns null if the line is not a timeout declaration.
 */
export function parseTimeoutLine(input, warnings) {
    const lexResult = JSDocLexer.tokenize(input);
    if (lexResult.errors.length > 0) {
        return null;
    }
    if (lexResult.tokens.length === 0) {
        return null;
    }
    const firstToken = lexResult.tokens[0];
    if (firstToken.tokenType !== TimeoutTag) {
        return null;
    }
    parserInstance.input = lexResult.tokens;
    const cst = parserInstance.timeoutLine();
    if (parserInstance.errors.length > 0) {
        const firstError = parserInstance.errors[0];
        const truncatedInput = input.length > 60 ? input.substring(0, 60) + '...' : input;
        warnings.push(`Failed to parse timeout line: "${truncatedInput}"\n` +
            `  Error: ${firstError.message}\n` +
            `  Expected format: @timeout "duration"`);
        return null;
    }
    return visitorInstance.visit(cst);
}
/**
 * Parse a @throttle line and return structured result.
 * Returns null if the line is not a throttle declaration.
 */
/**
 * Get serialized grammar productions for documentation/diagrams.
 */
export function getTriggerCancelGrammar() {
    return parserInstance.getSerializedGastProductions();
}
export function parseThrottleLine(input, warnings) {
    const lexResult = JSDocLexer.tokenize(input);
    if (lexResult.errors.length > 0) {
        return null;
    }
    if (lexResult.tokens.length === 0) {
        return null;
    }
    const firstToken = lexResult.tokens[0];
    if (firstToken.tokenType !== ThrottleTag) {
        return null;
    }
    parserInstance.input = lexResult.tokens;
    const cst = parserInstance.throttleLine();
    if (parserInstance.errors.length > 0) {
        const firstError = parserInstance.errors[0];
        const truncatedInput = input.length > 60 ? input.substring(0, 60) + '...' : input;
        warnings.push(`Failed to parse throttle line: "${truncatedInput}"\n` +
            `  Error: ${firstError.message}\n` +
            `  Expected format: @throttle limit=<number> period="duration"`);
        return null;
    }
    return visitorInstance.visit(cst);
}
//# sourceMappingURL=trigger-cancel-parser.js.map