// engine/onboarding/new-project-resolver.ts — SPEC-1021 safe new-project onboarding
import { mkdir } from 'node:fs/promises';
import path, { resolve } from 'node:path';
import { findDocsEntry, loadDocsRegistry } from '../web-fetcher/registry-loader.js';
const NONE_LABELS = new Set(['none', 'no framework', 'ninguno', 'sin framework', 'n/a']);
const UNSURE_LABELS = new Set(['not sure', 'unsure', 'no estoy seguro', 'not-sure']);
function answer(input, key) {
    const value = input.clarificationAnswers?.[key] ?? input.clarificationAnswers?.[key.toLowerCase()];
    return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
}
function valueOrAnswer(explicit, input, key) {
    if (typeof explicit === 'string' && explicit.trim().length > 0) {
        return explicit.trim();
    }
    return answer(input, key);
}
function normalizeNullableTech(value) {
    if (value === null) {
        return null;
    }
    const trimmed = value?.trim();
    if (!trimmed) {
        return undefined;
    }
    const lower = trimmed.toLowerCase();
    if (NONE_LABELS.has(lower)) {
        return null;
    }
    if (UNSURE_LABELS.has(lower)) {
        return undefined;
    }
    return trimmed;
}
export function slugifyAppName(input) {
    return input
        .trim()
        .toLowerCase()
        .replace(/[^a-z0-9._-]+/g, '-')
        .replace(/[-.]{2,}/g, '-')
        .replace(/^[-.]+|[-.]+$/g, '');
}
export function validateAppSlug(slug) {
    if (!slug) {
        return 'App folder is required.';
    }
    if (slug === '.' || slug === '..' || slug.includes('/') || slug.includes('\\')) {
        return 'App folder must be a simple folder name, not a path.';
    }
    if (slug.includes('..')) {
        return 'App folder cannot contain path traversal.';
    }
    return null;
}
function isWinPath(value) {
    return /^[a-zA-Z]:[\\/]/.test(value) || value.startsWith('\\\\');
}
export function resolveNewProjectPath(parentWorkspacePath, appSlug) {
    const slugError = validateAppSlug(appSlug);
    if (slugError) {
        throw new Error(slugError);
    }
    if (isWinPath(parentWorkspacePath)) {
        return path.win32.normalize(path.win32.join(parentWorkspacePath, appSlug));
    }
    return resolve(parentWorkspacePath, appSlug);
}
function buildQuestion(header, question, recommended, alternate, fallback) {
    return {
        header,
        question,
        multiSelect: false,
        options: [
            { label: `${recommended} (Recommended)`, description: `Use "${recommended}".` },
            { label: alternate, description: `Use "${alternate}".` },
            { label: fallback, description: `Use "${fallback}" or provide another value.` },
        ],
    };
}
function buildRequiredQuestions(input, resolved) {
    const suggestedSlug = slugifyAppName(input.appName ?? input.description).slice(0, 48) || 'new-app';
    const specs = [
        [
            !resolved.parentWorkspacePath,
            buildQuestion('Parent', 'Which parent workspace should contain the new app folder?', process.cwd(), input.projectPath ?? './', 'Custom path'),
        ],
        [
            !resolved.appSlug,
            buildQuestion('App Folder', 'What should the new app folder be called?', suggestedSlug, 'api', 'Custom folder'),
        ],
        [
            !resolved.projectType,
            buildQuestion('Project Type', 'What type of project are you creating?', 'api', 'web app', 'Custom type'),
        ],
        [
            !resolved.platform,
            buildQuestion('Platform', 'Where will this project run?', 'server', 'web', 'Custom platform'),
        ],
        [
            !resolved.languageAnswered,
            buildQuestion('Language', 'Which programming language should this project use?', 'Not sure', 'Use project/team default', 'Custom language'),
        ],
        [
            !resolved.frameworkAnswered,
            buildQuestion('Framework', 'Which framework should this project use?', 'Not sure', 'No framework', 'Custom framework'),
        ],
    ];
    return specs.filter(([missing]) => missing).map(([, question]) => question);
}
function buildCreateDirectoryQuestion() {
    return {
        header: 'Create Dir',
        question: 'May Planu create the project folder if it does not exist?',
        multiSelect: false,
        options: [
            { label: 'yes (Recommended)', description: 'Create only the resolved app folder.' },
            { label: 'no', description: 'Do not create folders; I will create it myself.' },
            { label: 'already exists', description: 'The project folder already exists.' },
        ],
    };
}
export function buildNewProjectQuestions(input, resolved) {
    const questions = buildRequiredQuestions(input, resolved);
    if (!resolved.createDirectoryAnswered) {
        questions.push(buildCreateDirectoryQuestion());
    }
    return questions;
}
async function verifyDocs(names) {
    const registry = await loadDocsRegistry();
    return names
        .filter((name) => name.trim().length > 0)
        .map((name) => {
        const entry = findDocsEntry(name, registry);
        if (entry) {
            return { name, url: entry.base, status: 'verified' };
        }
        return {
            name,
            url: '',
            status: 'unverified',
            reason: 'No official docs registry entry found.',
        };
    });
}
function collectResolvedFields(input) {
    const parentWorkspacePath = valueOrAnswer(input.parentWorkspacePath, input, 'Parent') ??
        valueOrAnswer(input.projectPath, input, 'Parent');
    const appName = valueOrAnswer(input.appName, input, 'App Folder');
    const appSlug = valueOrAnswer(input.appSlug, input, 'App Folder') ??
        (appName ? slugifyAppName(appName) : undefined);
    const projectType = valueOrAnswer(input.projectType, input, 'Project Type');
    const platform = valueOrAnswer(input.platform, input, 'Platform');
    const rawLanguage = valueOrAnswer(input.language, input, 'Language');
    const rawFramework = input.framework === null ? null : valueOrAnswer(input.framework, input, 'Framework');
    const rawDatabase = input.database === null ? null : valueOrAnswer(input.database, input, 'Database');
    const language = normalizeNullableTech(rawLanguage);
    const framework = normalizeNullableTech(rawFramework);
    const database = normalizeNullableTech(rawDatabase);
    const createDirAnswer = answer(input, 'Create Dir')?.toLowerCase();
    const languageAnswered = rawLanguage !== undefined;
    const frameworkAnswered = rawFramework !== undefined;
    const createDirectoryAnswered = input.createDirectory !== undefined || createDirAnswer !== undefined;
    const createDirectory = input.createDirectory === true ||
        createDirAnswer === 'yes' ||
        createDirAnswer === 'yes (recommended)' ||
        createDirAnswer === 'already exists';
    const errors = appSlug ? [validateAppSlug(appSlug)].filter((e) => Boolean(e)) : [];
    const resolvedProjectPath = parentWorkspacePath && appSlug && errors.length === 0
        ? resolveNewProjectPath(parentWorkspacePath, appSlug)
        : undefined;
    return {
        parentWorkspacePath,
        appName,
        appSlug,
        projectType,
        platform,
        language: language ?? undefined,
        framework,
        database,
        languageAnswered,
        frameworkAnswered,
        createDirectoryAnswered,
        createDirectory,
        resolvedProjectPath,
        errors,
    };
}
function isReady(fields) {
    return Boolean(fields.parentWorkspacePath &&
        fields.appSlug &&
        fields.projectType &&
        fields.platform &&
        fields.languageAnswered &&
        fields.frameworkAnswered &&
        fields.createDirectoryAnswered &&
        fields.createDirectory &&
        fields.resolvedProjectPath &&
        fields.errors.length === 0);
}
async function buildTechnologyContract(input, fields) {
    const resolvedProjectPath = fields.resolvedProjectPath;
    if (!resolvedProjectPath) {
        throw new Error('Resolved project path is required to build a technology contract.');
    }
    const docNames = [fields.language, fields.framework, fields.database].filter((name) => typeof name === 'string' && name.length > 0);
    const officialDocs = await verifyDocs(docNames);
    return {
        mode: 'new_project',
        projectPath: resolvedProjectPath,
        parentWorkspacePath: fields.parentWorkspacePath,
        appName: fields.appName,
        appSlug: fields.appSlug,
        projectType: fields.projectType,
        platform: fields.platform,
        language: fields.language,
        framework: fields.framework ?? null,
        database: fields.database ?? null,
        source: 'user-confirmed',
        evidence: [
            `User requested a new project: ${input.description}`,
            `Resolved project folder: ${resolvedProjectPath}`,
        ],
        officialDocs,
        verifiedAt: new Date().toISOString(),
    };
}
export async function resolveNewProjectOnboarding(input) {
    const fields = collectResolvedFields(input);
    if (!isReady(fields)) {
        return {
            ready: false,
            parentWorkspacePath: fields.parentWorkspacePath,
            resolvedProjectPath: fields.resolvedProjectPath,
            appSlug: fields.appSlug,
            questions: buildNewProjectQuestions(input, {
                parentWorkspacePath: fields.parentWorkspacePath,
                appSlug: fields.appSlug,
                projectType: fields.projectType,
                platform: fields.platform,
                language: fields.language,
                framework: fields.framework,
                languageAnswered: fields.languageAnswered,
                frameworkAnswered: fields.frameworkAnswered,
                createDirectoryAnswered: fields.createDirectoryAnswered,
            }),
            errors: fields.errors,
        };
    }
    const resolvedProjectPath = fields.resolvedProjectPath;
    if (!resolvedProjectPath) {
        throw new Error('Resolved project path is required before directory creation.');
    }
    await mkdir(resolvedProjectPath, { recursive: true });
    const contract = await buildTechnologyContract(input, fields);
    return {
        ready: true,
        parentWorkspacePath: fields.parentWorkspacePath,
        resolvedProjectPath: fields.resolvedProjectPath,
        appSlug: fields.appSlug,
        questions: [],
        contract,
        errors: fields.errors,
    };
}
//# sourceMappingURL=new-project-resolver.js.map