/**
 * ClassifyIntoLaneUseCase — spec 097, FR-26.
 *
 * Hybrid lane classifier: deterministic rules first (title-prefix, body-
 * keyword, label-hint heuristics), with agent fallback only when the rules
 * report `unknown` or low-confidence multi-lane match. Returns the
 * TypeSpec-defined `ContributorLane` enum end-to-end (NFR-3 — no raw
 * strings for domain concepts).
 *
 * Mirrors the pattern used by `auto-triage-intake-item`: cheap rule
 * cascade for the easy cases, LLM escalation only when judgment is
 * actually needed.
 */
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { inject, injectable } from 'tsyringe';
import { ContributorLane } from '../../../domain/generated/output.js';
const TITLE_PREFIX_LANES = [
    [/^(docs?|documentation)\s*[:(-]/i, ContributorLane.Docs],
    [/^(web|ui|ux|frontend)\s*[:(-]/i, ContributorLane.Ui],
    [/^(cli|terminal)\s*[:(-]/i, ContributorLane.Cli],
    [/^(agents?|llm|ai)\s*[:(-]/i, ContributorLane.Agents],
    [/^(infra|build|ci|cd|deps|chore|release)\s*[:(-]/i, ContributorLane.Infra],
];
const LABEL_LANE_HINTS = {
    docs: ContributorLane.Docs,
    documentation: ContributorLane.Docs,
    web: ContributorLane.Ui,
    ui: ContributorLane.Ui,
    frontend: ContributorLane.Ui,
    cli: ContributorLane.Cli,
    agents: ContributorLane.Agents,
    agent: ContributorLane.Agents,
    ai: ContributorLane.Agents,
    infra: ContributorLane.Infra,
    ci: ContributorLane.Infra,
    build: ContributorLane.Infra,
    deps: ContributorLane.Infra,
};
const BODY_KEYWORD_LANES = [
    [/\b(prompt|llm|agent[a-z\s]+)\b/i, ContributorLane.Agents],
    [/\b(react|component|storybook|tailwind|web ui|dashboard)\b/i, ContributorLane.Ui],
    [/\b(commander|ts-node|cli command|terminal)\b/i, ContributorLane.Cli],
    [/\b(typespec|migration|sqlite|github actions|workflow)\b/i, ContributorLane.Infra],
    [/\b(readme|docs|markdown|tutorial|documentation)\b/i, ContributorLane.Docs],
];
let ClassifyIntoLaneUseCase = class ClassifyIntoLaneUseCase {
    agentProvider;
    constructor(agentProvider) {
        this.agentProvider = agentProvider;
    }
    async execute(input) {
        const ruleHit = applyRules(input);
        if (ruleHit) {
            return { lane: ruleHit.lane, source: 'rules', rationale: ruleHit.rationale };
        }
        return await this.classifyWithAgent(input);
    }
    async classifyWithAgent(input) {
        const executor = await this.agentProvider.getExecutor();
        const allowed = Object.values(ContributorLane).join(', ');
        const prompt = [
            'Classify the GitHub issue below into exactly ONE Shep contributor lane.',
            `Allowed lanes: ${allowed}.`,
            'Respond with JSON: { "lane": "<lane>", "rationale": "<one short sentence>" }.',
            'Use lowercase lane values that match the allowed list verbatim.',
            '',
            `Title: ${input.title}`,
            `Body: ${input.body}`,
            input.existingLabels?.length ? `Labels: ${input.existingLabels.join(', ')}` : '',
        ]
            .filter(Boolean)
            .join('\n');
        const result = await executor.execute(prompt, {
            outputSchema: {
                type: 'object',
                properties: {
                    lane: { type: 'string' },
                    rationale: { type: 'string' },
                },
                required: ['lane'],
            },
        });
        const parsed = parseAgentResponse(result.result);
        return {
            lane: parsed.lane,
            source: 'agent',
            rationale: parsed.rationale ?? 'Classified via contributor-onboarding agent.',
        };
    }
};
ClassifyIntoLaneUseCase = __decorate([
    injectable(),
    __param(0, inject('IAgentExecutorProvider')),
    __metadata("design:paramtypes", [Object])
], ClassifyIntoLaneUseCase);
export { ClassifyIntoLaneUseCase };
function applyRules(input) {
    for (const [pattern, lane] of TITLE_PREFIX_LANES) {
        if (pattern.test(input.title)) {
            return { lane, rationale: `Title prefix matched ${lane} lane.` };
        }
    }
    const labelHits = new Set();
    for (const raw of input.existingLabels ?? []) {
        const lane = LABEL_LANE_HINTS[raw.toLowerCase()];
        if (lane)
            labelHits.add(lane);
    }
    if (labelHits.size === 1) {
        const [lane] = labelHits;
        return { lane, rationale: `Single matching label routed to ${lane}.` };
    }
    const bodyHits = new Set();
    for (const [pattern, lane] of BODY_KEYWORD_LANES) {
        if (pattern.test(input.body))
            bodyHits.add(lane);
    }
    if (bodyHits.size === 1) {
        const [lane] = bodyHits;
        return { lane, rationale: `Body keyword routed to ${lane}.` };
    }
    return null;
}
function parseAgentResponse(raw) {
    const candidate = JSON.parse(raw);
    const laneValue = String(candidate.lane ?? '').toLowerCase();
    const allowed = new Set(Object.values(ContributorLane));
    if (!allowed.has(laneValue)) {
        throw new Error(`Agent returned invalid lane: ${JSON.stringify(candidate.lane)}`);
    }
    return {
        lane: laneValue,
        rationale: typeof candidate.rationale === 'string' ? candidate.rationale : undefined,
    };
}
