import { elicitOrFallback, buildEnumSchema } from '../engine/elicitation/elicit-helper.js';
import { ti } from '../i18n/index.js';
import { knowledgeStore } from '../storage/index.js';
import { randomUUID } from 'node:crypto';
import { isShortTopic, generateMetaQuestions, detectContradictions, buildSuggestedAnswers, } from './clarify-requirements/questions.js';
import { generateQuestions } from './clarify-requirements/questions-context.js';
import { detectStack, shouldSkipQuestion, attachOptions, buildSuggestedDescription, } from './clarify-requirements/multiple-choice.js';
import { compactJson, compactResult, compactError } from './output-formatter.js';
/** Resume an existing clarification session with new answers. */
async function handleResumeSession(projectId, sessionId, newAnswers, knowledge) {
    const existing = await knowledgeStore.getClarification(projectId, sessionId).catch(() => null);
    if (!existing) {
        return compactError(`Session '${sessionId}' not found. Start a new session by calling clarify_requirements without sessionId.`);
    }
    const mergedAnswers = { ...existing.answers, ...newAnswers };
    const contradictions = Object.keys(mergedAnswers).length > 0 ? detectContradictions(mergedAnswers) : [];
    const criticalIds = existing.questions
        .filter((q) => q.importance === 'critical')
        .map((q) => q.id);
    const specReady = criticalIds.every((id) => {
        const ans = mergedAnswers[id];
        return ans !== undefined && ans.trim() !== '';
    });
    const enriched = existing.questions.map((q) => ({
        ...q,
        suggestedAnswers: q.suggestedAnswers ?? buildSuggestedAnswers(q, knowledge),
    }));
    const suggestedDescription = specReady
        ? buildSuggestedDescription(existing.topic, mergedAnswers, enriched)
        : undefined;
    const updated = {
        ...existing,
        questions: enriched,
        answers: mergedAnswers,
        isComplete: specReady,
        contradictions: contradictions.length > 0 ? contradictions : undefined,
        specReady,
        interviewMode: 'multiple-choice',
        ...(suggestedDescription !== undefined ? { suggestedDescription } : {}),
    };
    await knowledgeStore.addClarification(projectId, updated);
    const result = {
        sessionId: existing.id,
        topic: existing.topic,
        interviewMode: 'multiple-choice',
        questionsCount: existing.questions.length,
        questions: enriched.map((q) => ({
            id: q.id,
            question: q.question,
            category: q.category,
            importance: q.importance,
            suggestedAnswers: q.suggestedAnswers,
            options: q.options,
            skipped: q.skipped,
            answered: mergedAnswers[q.id] !== undefined,
        })),
        specReady,
        ...(suggestedDescription !== undefined ? { suggestedDescription } : {}),
        answeredCount: Object.keys(mergedAnswers).length,
        ...(contradictions.length > 0
            ? {
                contradictions,
                contradictionWarning: 'Potential contradictions detected — review before creating the spec.',
            }
            : {}),
        instructions: specReady
            ? 'Requirements gathered. Present the suggestedDescription to the user and ask for explicit confirmation before calling create_spec. Do NOT call create_spec automatically.'
            : `${criticalIds.length - criticalIds.filter((id) => mergedAnswers[id]?.trim()).length} critical question(s) remaining.`,
        message: `Session resumed. ${Object.keys(mergedAnswers).length}/${existing.questions.length} answered.`,
        // SPEC-542: Confirmation required before calling create_spec
        ...(specReady
            ? {
                confirmationRequired: {
                    needsConfirmation: true,
                    question: 'Do you want me to create the spec with these gathered requirements?',
                    header: 'Create Spec?',
                    options: [
                        {
                            label: 'Yes, create the spec',
                            description: 'Proceed to call create_spec with the clarificationSessionId.',
                        },
                        {
                            label: 'No, review first',
                            description: 'Review or adjust the requirements before creating the spec.',
                        },
                    ],
                    note: 'This tool does NOT create specs automatically. create_spec must be called separately after explicit user confirmation.',
                },
            }
            : {}),
    };
    return compactResult(compactJson(result, 200));
}
export async function handleClarifyRequirements(params, server) {
    const { projectId, topic, context, maxQuestions = 7, answers, sessionId } = params;
    try {
        // Load project knowledge — proceed with null if project not yet initialized (SPEC-020: criterion 5.1)
        const knowledge = await knowledgeStore.getKnowledge(projectId).catch(() => null);
        // Resume existing session if sessionId provided
        if (sessionId) {
            return await handleResumeSession(projectId, sessionId, answers ?? {}, knowledge);
        }
        // Detect contradictions in provided answers (if any)
        const contradictions = answers && Object.keys(answers).length > 0 ? detectContradictions(answers) : [];
        // If topic is vague (< 10 words), prepend meta-questions
        const metaQuestions = isShortTopic(topic)
            ? generateMetaQuestions()
            : [];
        // Generate main clarification questions (cap total at maxQuestions)
        const mainMaxQ = Math.max(1, maxQuestions - metaQuestions.length);
        const mainQuestions = generateQuestions(topic, context, knowledge, mainMaxQ);
        const rawQuestions = [...metaQuestions, ...mainQuestions].slice(0, maxQuestions);
        // Detect project stack for auto-skip and option filtering (SPEC-337)
        const projectPath = knowledge?.projectPath ?? '';
        const stack = projectPath !== ''
            ? await detectStack(projectPath).catch(() => ({
                framework: null,
                language: null,
                database: null,
                detectedLibs: [],
            }))
            : { framework: null, language: null, database: null, detectedLibs: [] };
        // Apply multiple-choice options + mark skipped questions (SPEC-337)
        const questions = rawQuestions.map((q) => {
            const withOptions = attachOptions(q);
            const skipped = shouldSkipQuestion(q, stack);
            return skipped ? { ...withOptions, skipped: true } : withOptions;
        });
        // Determine if spec is ready (all non-skipped critical questions have non-empty answers)
        const criticalIds = questions
            .filter((q) => q.importance === 'critical' && q.skipped !== true)
            .map((q) => q.id);
        const specReady = answers !== undefined &&
            criticalIds.every((id) => {
                const ans = answers[id];
                return ans !== undefined && ans.trim() !== '';
            });
        // Build suggestedDescription when spec is ready (SPEC-337)
        const currentAnswers = answers ?? {};
        const suggestedDescription = specReady
            ? buildSuggestedDescription(topic, currentAnswers, questions)
            : undefined;
        // Create a clarification session
        const session = {
            id: randomUUID().slice(0, 8),
            projectId,
            topic,
            questions,
            answers: currentAnswers,
            isComplete: specReady,
            createdAt: new Date().toISOString(),
            contradictions: contradictions.length > 0 ? contradictions : undefined,
            specReady,
            interviewMode: 'multiple-choice',
            ...(suggestedDescription !== undefined ? { suggestedDescription } : {}),
        };
        // Persist the session
        await knowledgeStore.addClarification(projectId, session);
        const result = {
            sessionId: session.id,
            topic,
            interviewMode: 'multiple-choice',
            questionsCount: questions.length,
            hasMetaQuestions: metaQuestions.length > 0,
            questions: questions.map((q) => ({
                id: q.id,
                question: q.question,
                category: q.category,
                importance: q.importance,
                suggestedAnswer: q.suggestedAnswer,
                /* v8 ignore next 1 */
                suggestedAnswers: q.suggestedAnswers ?? [],
                options: q.options,
                skipped: q.skipped,
            })),
            specReady,
            ...(suggestedDescription !== undefined ? { suggestedDescription } : {}),
            instructions: specReady
                ? 'Requirements gathered. Present the suggestedDescription to the user and ask for explicit confirmation before calling create_spec. Do NOT call create_spec automatically.'
                : 'Answer the questions above, then call create_spec with the gathered context. You can skip nice-to-have questions if the user does not have strong opinions.',
            message: ti('tools.clarify_requirements.success', {
                count: String(questions.length),
                topic,
            }),
        };
        if (contradictions.length > 0) {
            result.contradictions = contradictions;
            result.contradictionWarning =
                'Potential contradictions detected in your answers — please review before creating the spec.';
        }
        // SPEC-542: When requirements are ready, add a confirmation question so the LLM
        // MUST ask the user before calling create_spec — never create silently.
        if (specReady) {
            result.confirmationRequired = {
                needsConfirmation: true,
                question: 'Do you want me to create the spec with these gathered requirements?',
                header: 'Create Spec?',
                options: [
                    {
                        label: 'Yes, create the spec',
                        description: 'Proceed to call create_spec with the clarificationSessionId.',
                    },
                    {
                        label: 'No, review first',
                        description: 'Review or adjust the requirements before creating the spec.',
                    },
                ],
                note: 'This tool does NOT create specs automatically. create_spec must be called separately after explicit user confirmation.',
            };
        }
        // SPEC-595: Elicit the first unanswered critical question directly when client supports it
        if (server !== undefined && !specReady) {
            const firstPending = questions.find((q) => q.importance === 'critical' &&
                q.skipped !== true &&
                (!currentAnswers[q.id] || currentAnswers[q.id] === ''));
            if (firstPending !== undefined) {
                const options = firstPending.options ?? [];
                const enumValues = options.length > 0 && options.length <= 5
                    ? options.map((o) => typeof o === 'string'
                        ? o
                        : (o.value ??
                            o.label ??
                            String(o)))
                    : ['yes', 'no', 'unsure', 'other'];
                const enumNames = options.length > 0 && options.length <= 5
                    ? options.map((o) => typeof o === 'string' ? o : (o.label ?? String(o)))
                    : ['Yes', 'No', 'Unsure', 'Other'];
                const { field, property } = buildEnumSchema(firstPending.id, enumValues, enumNames, firstPending.question);
                const schema = { type: 'object', properties: { [field]: property } };
                const fallback = {
                    header: firstPending.category,
                    question: firstPending.question,
                    multiSelect: false,
                    options: enumValues.map((v, i) => ({
                        label: v,
                        description: enumNames[i] ?? v,
                    })),
                };
                const outcome = await elicitOrFallback(server, firstPending.question, schema, [fallback]);
                if (outcome.mode === 'fallback') {
                    return {
                        content: [
                            {
                                type: 'text',
                                text: `⚡ INTERACTIVE — call AskUserQuestion now with the interactiveQuestions from structuredContent`,
                            },
                        ],
                        structuredContent: {
                            ...result,
                            interactiveQuestions: outcome.interactiveQuestions,
                        },
                    };
                }
                const elicitedAnswer = outcome.action === 'accept'
                    ? (outcome.content?.[firstPending.id] ?? '')
                    : '';
                // Embed the elicited answer for the next call
                if (elicitedAnswer) {
                    result.elicitedAnswer = { questionId: firstPending.id, answer: elicitedAnswer };
                    result.instructions =
                        `Elicited answer for "${firstPending.id}": "${elicitedAnswer}". ` +
                            `Pass it in the answers map in the next clarify_requirements call.`;
                }
            }
        }
        return compactResult(compactJson(result, 200));
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        return {
            content: [{ type: 'text', text: ti('errors.internalError', { message }) }],
            isError: true,
        };
    }
}
//# sourceMappingURL=clarify-requirements.js.map