// tools/create-spec/spec-builder.ts — Builds the Spec object, diagrams, and scope filters.
// Extracted from create-spec.ts to keep file sizes within limits.
import { hashProjectPath, knowledgeStore, specStore, globalStore } from '../../storage/index.js';
import { getCurrentTier } from '../license-gate.js';
import { checkLimits } from '../../engine/license-validator.js';
import { inferDifficulty, calculateEstimation } from '../../engine/estimator.js';
import { generateDiagrams } from '../../engine/mermaid-generator.js';
import { analyzeSplit, formatSplitSuggestion } from '../../engine/spec-splitter.js';
import { getScopeFilters } from '../../engine/scope-filter.js';
import { isLegacyLocation, getSpecFileNames } from '../../engine/planu-config-writer.js';
import { slugify, generateSpecId, generateBranchName } from '../create-spec-helpers.js';
import { join } from 'node:path';
import { readdir, readFile } from 'node:fs/promises';
/** Builds the Spec object, calculates estimation, generates diagrams, and scope filters. */
export async function buildSpecContext(params) {
    const { title, description, projectPath, type = 'feature', scope = 'feature', target = 'fullstack', tags = [], feature, } = params;
    const projectId = hashProjectPath(projectPath);
    const knowledge = await knowledgeStore.getKnowledge(projectId);
    const existingSpecs = await specStore.listSpecs(projectId);
    // License: check active spec limit (exclude completed specs)
    const activeSpecs = existingSpecs.filter((s) => s.status !== 'done');
    const tier = await getCurrentTier();
    const specLimit = checkLimits(tier, activeSpecs.length, 'maxActiveSpecs');
    /* v8 ignore start -- license limit requires paid tier test env */
    if (!specLimit.allowed) {
        return { ok: false, errorMessage: specLimit.message };
    }
    /* v8 ignore stop */
    // Generate spec ID: combine store IDs + filesystem IDs for global max
    const fsSpecIds = await scanFilesystemSpecIds(projectPath);
    const allSpecs = [...existingSpecs, ...fsSpecIds.map((id) => ({ id }))];
    const specId = generateSpecId(allSpecs);
    const slug = slugify(title);
    // Check for duplicate title
    const duplicate = existingSpecs.find((s) => slugify(s.title) === slug);
    // Infer difficulty and calculate estimation
    const difficulty = inferDifficulty(type, scope);
    const globalConfig = await globalStore.getGlobalConfig();
    const estimation = calculateEstimation(type, scope, difficulty, globalConfig);
    // Determine risk level from difficulty and scope
    const risk = difficulty >= 4 || scope === 'architectural'
        ? 'high'
        : difficulty >= 3 || scope === 'cross-module'
            ? 'medium'
            : 'low';
    // Build spec location with naming convention detection
    const specLocation = knowledge?.specLocation ?? 'planu/specs';
    const naming = isLegacyLocation(specLocation) ? 'legacy' : 'modern';
    const fileNames = getSpecFileNames(naming);
    const featureSlug = feature ? slugify(feature) : '_general';
    const specDir = join(projectPath, specLocation, featureSlug, slug);
    const specPath = join(specDir, fileNames.spec);
    const technicalPath = join(specDir, fileNames.technical);
    const progressPath = join(specDir, fileNames.progress);
    // Generate git branch name
    const gitBranch = generateBranchName(specId, slug, type);
    // Build the Spec object
    const now = new Date().toISOString();
    const spec = {
        id: specId,
        title,
        slug,
        feature: featureSlug,
        type,
        scope,
        status: 'draft',
        difficulty,
        risk,
        projectId,
        createdAt: now,
        updatedAt: now,
        specPath,
        technicalPath,
        progressPath,
        estimation,
        actuals: null,
        target,
        tags,
        dependencies: [],
        blockedBy: [],
        gitBranch,
        impactAnalysis: null,
    };
    // Detect context flags for diagrams and filters
    const { detectContextFlags } = await import('../create-spec-tech.js');
    const contextFlags = detectContextFlags(spec, knowledge);
    // Generate Mermaid diagrams
    const diagrams = generateDiagrams(spec, knowledge, contextFlags);
    // Analyze if spec should be split
    const splitSuggestion = analyzeSplit({
        criteriaCount: 4,
        moduleCount: scope === 'cross-module' ? 5 : scope === 'architectural' ? 6 : 2,
        estimatedHours: estimation.devHours,
        description,
        type,
        scope,
        target,
    }, specId);
    // Get scope filter configuration
    const scopeFilters = getScopeFilters(scope, {
        hasDatabase: contextFlags.hasDatabase,
        hasUi: contextFlags.hasUi,
    });
    return {
        ok: true,
        context: {
            spec,
            specDir,
            specPath,
            technicalPath,
            progressPath,
            diagrams,
            estimation,
            splitSuggestion,
            scopeFilters,
            duplicate,
            projectId,
        },
    };
}
/** Builds the split suggestion result object if applicable. */
export function buildSplitResult(splitSuggestion, experienceLevel) {
    if (!splitSuggestion) {
        return undefined;
    }
    const level = experienceLevel ?? 'intermediate';
    return {
        shouldSplit: true,
        reason: splitSuggestion.reason,
        subSpecs: splitSuggestion.suggestedSpecs,
        formatted: formatSplitSuggestion(splitSuggestion, level === 'beginner' ? 'beginner' : 'expert'),
    };
}
/** Scan filesystem for SPEC-XXX directories to find max ID globally. */
async function scanFilesystemSpecIds(projectPath) {
    const ids = [];
    try {
        const configPath = join(projectPath, 'planu.json');
        let specLocation = 'planu/specs';
        try {
            const config = JSON.parse(await readFile(configPath, 'utf-8'));
            specLocation = config.specLocation ?? specLocation;
        }
        catch {
            /* no planu.json — use default */
        }
        const specsRoot = join(projectPath, specLocation);
        const features = await readdir(specsRoot, { withFileTypes: true }).catch(() => []);
        for (const feat of features) {
            if (!feat.isDirectory()) {
                continue;
            }
            const featDir = join(specsRoot, feat.name);
            const entries = await readdir(featDir, { withFileTypes: true }).catch(() => []);
            for (const entry of entries) {
                const match = /^(SPEC-\d+)/.exec(entry.name);
                if (match?.[1]) {
                    ids.push(match[1]);
                }
            }
        }
        // Also check legacy location
        const legacyRoot = join(projectPath, 'docs/sdd/specs');
        const legacyEntries = await readdir(legacyRoot, { withFileTypes: true }).catch(() => []);
        for (const entry of legacyEntries) {
            const match = /^(SPEC-\d+)/.exec(entry.name);
            if (match?.[1]) {
                ids.push(match[1]);
            }
        }
    }
    catch {
        /* filesystem scan is best-effort */
    }
    return ids;
}
//# sourceMappingURL=spec-builder.js.map