"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChatFirstController = void 0;
const path = __importStar(require("path"));
const fs = __importStar(require("fs-extra"));
const FeatureGraphCompiler_1 = require("../services/FeatureGraphCompiler");
const ImplGraphCompiler_1 = require("../services/ImplGraphCompiler");
const PipelineOrchestrator_1 = require("../services/PipelineOrchestrator");
const QuestionGraphEngine_1 = require("../services/QuestionGraphEngine");
const RegistryDiffEngine_1 = require("../services/RegistryDiffEngine");
const ExecutionHistory_1 = require("../services/ExecutionHistory");
const LivingDictionary_1 = require("../services/LivingDictionary");
const DoctorEngine_1 = require("../doctor/DoctorEngine");
const TUIBrainService_1 = require("./TUIBrainService");
const AICore_1 = require("../core/ai/AICore");
const LandingPageOrchestrator_1 = require("../services/LandingPageOrchestrator");
class ChatFirstController {
    constructor(projectRoot = process.cwd()) {
        this.projectRoot = projectRoot;
        this.brain = new TUIBrainService_1.TUIBrainService(projectRoot);
    }
    getPendingPlan() {
        return this.pendingPlan;
    }
    clearPendingPlan() {
        this.pendingPlan = undefined;
    }
    async routeIntent(input) {
        const normalized = input.trim().toLowerCase();
        // Fast paths for obvious exact commands to save latency
        if (/^(verify|validate|doctor|status)$/.test(normalized)) {
            return { action: "verify", reason: "Exact match for verification" };
        }
        if (/^compile (landing )?page$/.test(normalized)) {
            return { action: "compile_landing_page", reason: "Exact match for page compilation" };
        }
        // Fast path for obvious error stack traces or pasted error logs
        if (normalized.includes("typeerror:") ||
            normalized.includes("referenceerror:") ||
            normalized.includes("error ts") ||
            normalized.includes("unhandled runtime error") ||
            /\b[a-z0-9_-]+\.(?:tsx|ts|js):\d+:\d+/i.test(normalized) ||
            /\b[a-z0-9_-]+\.(?:tsx|ts|js)\s+\(\d+:\d+\)/i.test(normalized) ||
            /at \S+ \(.+?:\d+:\d+\)/.test(input) ||
            normalized.includes("stack trace")) {
            return { action: "heal_runtime_error", reason: "Detected pasted error output or stack trace" };
        }
        const aiCore = AICore_1.AICore.getInstance({
            fallbackEnabled: true,
            workingDirectory: this.projectRoot,
        });
        const prompt = `You are the intent router for the MyContext AI CLI agent.
User Input: "${input}"

Map the input to EXACTLY one of these actions:
- "explore": user is asking questions, exploring, or roasting the codebase (what, why, how, thoughts)
- "clarify": user is answering a blocking question (e.g. q-123) or clarifying blockers
- "verify": user wants to run health checks, validate, doctor, or check status
- "compile_landing_page": user wants to build/compile/generate a page or landing page
- "execute": user explicitly approved execution ("build it", "go ahead", "ship it")
- "heal_runtime_error": user pasted a compiler error, typescript error, runtime crash error, or stack trace (e.g. "TypeError: Cannot read properties of undefined", "error TS2345", etc.) and wants it fixed.
- "plan": default for creating new features, apps, or describing tasks

Return ONLY raw JSON matching this exact structure:
\`\`\`json
{
  "action": "plan",
  "reason": "Brief explanation of why this action was chosen"
}
\`\`\``;
        try {
            const response = await aiCore.generateText(prompt, { temperature: 0.1 });
            let clean = response.trim();
            if (clean.startsWith("\`\`\`json")) {
                clean = clean.replace(/^\`\`\`json\s*/m, "").replace(/\s*\`\`\`$/m, "");
            }
            else if (clean.startsWith("\`\`\`")) {
                clean = clean.replace(/^\`\`\`\s*/m, "").replace(/\s*\`\`\`$/m, "");
            }
            const parsed = JSON.parse(clean);
            const validActions = ["explore", "clarify", "verify", "compile_landing_page", "execute", "plan", "heal_runtime_error"];
            if (validActions.includes(parsed.action)) {
                return parsed;
            }
        }
        catch (e) {
            // Fallback intentionally to plan if parsing fails
        }
        return { action: "plan", reason: "Default fallback due to unclassifiable intent" };
    }
    async explore(input) {
        const brain = await this.brain.load();
        const name = brain.project?.name ||
            brain.metadata?.projectConfig?.name ||
            brain.project_name ||
            path.basename(this.projectRoot);
        const featureCount = Array.isArray(brain.features) ? brain.features.length : 0;
        const pending = this.pendingPlan
            ? `There is a pending plan for "${this.pendingPlan.nextFeature?.name || this.pendingPlan.prompt}".`
            : "There is no pending plan.";
        return [
            `${name} is guarded by the Living Brain and compiler pipeline.`,
            `Current Brain has ${featureCount} feature(s). ${pending}`,
            `For build work, describe the desired outcome. I will preview Brain changes first, then execute only after approval.`,
            `Request received: ${input}`,
        ];
    }
    async plan(input) {
        const currentBrain = await this.brain.load();
        const questionDelta = await this.buildQuestionDelta(input);
        const feature = this.draftFeature(currentBrain, input);
        const delta = {
            ...questionDelta,
        };
        if (feature) {
            delta.features = [feature];
        }
        const mutation = await this.brain.previewMutation(`TUI plan: ${this.cleanPromptTitle(input)}`, currentBrain, delta);
        const blockers = this.extractBlockers(mutation.proposedBrain);
        const { nextFeature, batches } = await this.compilePlanPreview(mutation.proposedBrain);
        this.pendingPlan = {
            prompt: input,
            mutation,
            blockers,
            nextFeature,
            batches,
        };
        return this.pendingPlan;
    }
    async commitPlan(plan) {
        const result = await this.brain.applyMutation(plan.mutation);
        if (!result.success) {
            throw new Error(`Could not save plan: ${result.errors.join("; ")}`);
        }
    }
    async execute(prompt) {
        const executionPrompt = prompt?.trim() ||
            this.pendingPlan?.prompt ||
            "Execute the next pending MyContext feature";
        const orchestrator = new PipelineOrchestrator_1.PipelineOrchestrator();
        const pipeline = await orchestrator.executeFromPrompt(executionPrompt);
        const verification = await this.verify();
        this.pendingPlan = undefined;
        return { pipeline, verification };
    }
    async verify() {
        const [doctor, registry, graphSummary, semantics] = await Promise.all([
            this.runDoctorCheck(),
            this.runRegistryDiff(),
            this.runGraphValidation(),
            this.runSemanticValidation(),
        ]);
        const errors = doctor.diagnostics.filter((d) => d.severity === "error").length;
        const warnings = doctor.diagnostics.filter((d) => d.severity === "warning").length;
        return {
            doctor: {
                score: doctor.score,
                grade: doctor.grade,
                errors,
                warnings,
            },
            registry: {
                summary: registry.summary,
                driftItems: registry.items.length,
                hasUntrackedFiles: registry.hasUntrackedFiles,
                hasOrphanRefs: registry.hasOrphanRefs,
            },
            graph: graphSummary,
            semantics,
            needsRepair: errors > 0 ||
                registry.hasUntrackedFiles ||
                registry.hasOrphanRefs ||
                graphSummary.gaps > 0 ||
                semantics.conflicts > 0,
        };
    }
    async clarify(input) {
        const match = input.match(/\b(q-[a-z0-9-]+)\b/i);
        if (!match) {
            return this.currentBlockers();
        }
        const answer = input
            .replace(/^(answer|clarify)\s+/i, "")
            .replace(match[1], "")
            .replace(/^[:=\-\s]+/, "")
            .trim();
        if (!answer) {
            return this.currentBlockers();
        }
        const engine = new QuestionGraphEngine_1.QuestionGraphEngine({
            brainPath: path.join(this.projectRoot, ".mycontext", "context.json"),
        });
        await engine.load();
        engine.answerQuestion(match[1], answer, "tui");
        await engine.save();
        return this.currentBlockers();
    }
    async recentHistory(limit = 10) {
        const history = new ExecutionHistory_1.ExecutionHistory(this.projectRoot);
        return history.list({ limit });
    }
    async operationStatus(limit = 10) {
        const history = new ExecutionHistory_1.ExecutionHistory(this.projectRoot);
        return history.list({ type: "operation", limit });
    }
    async registryDiff() {
        return this.runRegistryDiff();
    }
    async currentBlockers() {
        const brain = await this.brain.load();
        return this.extractBlockers(brain);
    }
    async buildQuestionDelta(input) {
        const engine = new QuestionGraphEngine_1.QuestionGraphEngine({
            brainPath: path.join(this.projectRoot, ".mycontext", "context.json"),
        });
        await engine.load();
        await engine.harvestFromPrompt(input);
        const graph = engine.getGraph();
        return {
            context: {
                questions: {
                    nodes: Array.from(graph.nodes.values()).map((node) => ({
                        id: node.id,
                        text: node.text,
                        context: node.context,
                        severity: node.severity,
                        status: node.status,
                        answer: node.answer,
                        category: node.category,
                        options: node.options,
                        validation: node.validation,
                        source: node.source,
                        resolvedAt: node.resolvedAt,
                        resolvedBy: node.resolvedBy,
                        metadata: node.metadata,
                    })),
                    edges: graph.edges,
                    rootIds: graph.rootIds,
                    lastHarvested: new Date().toISOString(),
                },
            },
        };
    }
    draftFeature(brain, input) {
        const existing = Array.isArray(brain.features) ? brain.features : [];
        const name = this.toFeatureName(input);
        const normalizedName = name.toLowerCase();
        const alreadyExists = existing.some((feature) => String(feature.name || "").toLowerCase() === normalizedName);
        if (alreadyExists)
            return null;
        const baseSlug = this.slugify(name);
        const idBase = `FEAT-${baseSlug.toUpperCase().slice(0, 28) || "DREAM-APP"}`;
        const ids = new Set(existing.map((feature) => feature.id));
        let id = idBase;
        let suffix = 2;
        while (ids.has(id)) {
            id = `${idBase}-${suffix}`;
            suffix++;
        }
        const componentName = `${this.toPascalCase(baseSlug)}Shell`;
        const routePath = baseSlug.toLowerCase();
        const modelName = `${this.toPascalCase(baseSlug)}Record`;
        const actionName = `${this.toCamelCase(baseSlug)}Action`;
        const testName = `${baseSlug.toLowerCase()}-flow`;
        return {
            id,
            name,
            description: input.trim(),
            priority: "high",
            userValue: "Transforms the user's dream into a constrained, buildable first app slice.",
            status: "planned",
            ownership: {
                routes: [routePath],
                components: [componentName],
                actions: [actionName],
                models: [modelName],
                permissions: [],
                i18n: [],
                tests: [testName],
            },
            acceptanceCriteria: [
                `Users can complete the core flow described by: ${input.trim()}`,
                "Implementation is executed through MyContext operations, not direct TUI file writes",
                "Doctor, registry, graph, and semantic validation run after execution",
            ],
            dependencies: [],
        };
    }
    async compilePlanPreview(brain) {
        const featureCompiler = new FeatureGraphCompiler_1.FeatureGraphCompiler(path.join(this.projectRoot, ".mycontext", "context.json"));
        const graph = await featureCompiler.compileFromContext(brain);
        const target = graph.getNextRecommended();
        if (!target)
            return { batches: [] };
        const implCompiler = new ImplGraphCompiler_1.ImplGraphCompiler(path.join(this.projectRoot, ".mycontext", "context.json"));
        const impl = await implCompiler.compileFromFeature(target);
        return {
            nextFeature: {
                id: target.id,
                name: target.name,
                description: target.description,
            },
            batches: impl.batches.map((batch) => ({
                id: batch.id,
                taskCount: batch.tasks.length,
                tasks: batch.tasks.map((task) => task.description || task.name || task.id),
            })),
        };
    }
    extractBlockers(brain) {
        const nodes = brain.context?.questions?.nodes || [];
        return nodes
            .filter((node) => node.status !== "answered" && node.severity === "blocking")
            .map((node) => ({
            id: node.id,
            text: node.text,
            options: Array.isArray(node.options)
                ? node.options.map((option) => option.label || option.value)
                : undefined,
        }));
    }
    async runDoctorCheck() {
        return (0, DoctorEngine_1.runDoctor)(this.projectRoot, {});
    }
    async runRegistryDiff() {
        const engine = new RegistryDiffEngine_1.RegistryDiffEngine(this.projectRoot);
        return engine.diff();
    }
    async runGraphValidation() {
        const compiler = new FeatureGraphCompiler_1.FeatureGraphCompiler(path.join(this.projectRoot, ".mycontext", "context.json"));
        const graph = await compiler.compileFromContext();
        let gaps = 0;
        for (const feature of graph.getDependencyOrder()) {
            const featureGaps = await compiler.detectGaps(feature);
            gaps += featureGaps.length;
        }
        return { features: graph.nodes.size, gaps };
    }
    async runSemanticValidation() {
        const dict = new LivingDictionary_1.LivingDictionary(path.join(this.projectRoot, ".mycontext", "context.json"));
        await dict.load();
        return {
            terms: dict.getAllEntries().length,
            conflicts: dict.getConflicts().length,
        };
    }
    toFeatureName(input) {
        const cleaned = input
            .trim()
            .replace(/^(please\s+)?(build|create|make|add|generate|plan)\s+(me\s+)?/i, "")
            .replace(/\b(app|application|feature)\b$/i, "")
            .trim();
        const base = cleaned || input.trim() || "Dream App";
        return base
            .split(/\s+/)
            .slice(0, 8)
            .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
            .join(" ");
    }
    cleanPromptTitle(input) {
        return this.toFeatureName(input).slice(0, 80);
    }
    slugify(input) {
        const slug = input
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, "-")
            .replace(/^-+|-+$/g, "");
        return slug || "dream-app";
    }
    toPascalCase(slug) {
        return slug
            .split("-")
            .filter(Boolean)
            .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
            .join("") || "DreamApp";
    }
    toCamelCase(slug) {
        const pascal = this.toPascalCase(slug);
        return pascal.charAt(0).toLowerCase() + pascal.slice(1);
    }
    async getOutputDir() {
        let outputDir = path.join(this.projectRoot, "components/landing-page");
        if (await fs.pathExists(path.join(this.projectRoot, "apps/site"))) {
            outputDir = path.join(this.projectRoot, "apps/site/components/landing-page");
        }
        else if (await fs.pathExists(path.join(this.projectRoot, "../../apps/site"))) {
            outputDir = path.join(this.projectRoot, "../../apps/site/components/landing-page");
        }
        else if (await fs.pathExists(path.join(this.projectRoot, "src/components"))) {
            outputDir = path.join(this.projectRoot, "src/components/landing-page");
        }
        return outputDir;
    }
    async healError(errorInput) {
        const aiCore = AICore_1.AICore.getInstance({
            fallbackEnabled: true,
            workingDirectory: this.projectRoot,
        });
        const outputDir = await this.getOutputDir();
        // Load project contexts if available
        const contextPath = path.join(this.projectRoot, ".mycontext/landing-page/project-context.json");
        const narrativePath = path.join(this.projectRoot, ".mycontext/landing-page/narrative.json");
        let projectContext = null;
        let narrative = null;
        try {
            if (await fs.pathExists(contextPath)) {
                projectContext = await fs.readJSON(contextPath);
            }
            if (await fs.pathExists(narrativePath)) {
                narrative = await fs.readJSON(narrativePath);
            }
        }
        catch { }
        // Read relevant TSX/TS files in outputDir
        let projectFilesContext = "";
        const tsxFiles = [];
        try {
            if (await fs.pathExists(outputDir)) {
                const allFiles = await fs.readdir(outputDir);
                const filtered = allFiles.filter(f => f.endsWith(".tsx") || f.endsWith(".ts"));
                for (const file of filtered) {
                    const componentName = path.basename(file, path.extname(file)); // e.g. "TrustSection"
                    const isPage = file === "page.tsx";
                    const isMentioned = errorInput.includes(file) || errorInput.includes(componentName);
                    if (isPage || isMentioned) {
                        const filePath = path.join(outputDir, file);
                        const content = await fs.readFile(filePath, "utf8");
                        projectFilesContext += `\n--- FILE: ${file} ---\n${content}\n`;
                        tsxFiles.push(file);
                    }
                }
                // Fallback to all files if nothing was matched except page.tsx
                if (tsxFiles.length <= 1) {
                    projectFilesContext = "";
                    tsxFiles.length = 0;
                    for (const file of filtered) {
                        const filePath = path.join(outputDir, file);
                        const content = await fs.readFile(filePath, "utf8");
                        projectFilesContext += `\n--- FILE: ${file} ---\n${content}\n`;
                        tsxFiles.push(file);
                    }
                }
            }
        }
        catch (e) {
            return { success: false, healedFiles: [], error: `Could not read component files: ${e.message}` };
        }
        const prompt = `You are an expert TypeScript & Next.js React developer.
The user reported a runtime or compilation error in the application.

Here is the error pasted by the user:
"""
${errorInput}
"""

Here is the Project Context Manifest:
${JSON.stringify(projectContext, null, 2)}

Here is the Narrative Manifest:
${JSON.stringify(narrative, null, 2)}

Here are the current files in the landing page component directory:
${projectFilesContext}

Your task:
1. Analyze the error and determine which file(s) are causing it.
2. Fix the error by writing the correct, healed code for the offending file(s).
3. If a component is missing props (like trustStrategy), make sure to pass them from page.tsx using mock/narrative data, or make the props optional with defaults in the component itself.
4. Return a JSON object matching this TypeScript interface exactly:
\`\`\`ts
interface HealingResult {
  healedFiles: {
    filename: string; // just the filename e.g. "page.tsx" or "TrustSection.tsx"
    code: string; // the full corrected code for the file
  }[];
  explanation: string; // brief explanation of what was fixed and why
}
\`\`\`

Return ONLY the raw JSON object. Do not include any markdown formatting or extra text.`;
        try {
            const response = await aiCore.generateText(prompt, { temperature: 0.1 });
            let clean = response.trim();
            if (clean.startsWith("```json")) {
                clean = clean.replace(/^```json\s*/m, "").replace(/\s*```$/m, "");
            }
            else if (clean.startsWith("```")) {
                clean = clean.replace(/^```\s*/m, "").replace(/\s*```$/m, "");
            }
            const parsed = JSON.parse(clean);
            if (!parsed.healedFiles || !Array.isArray(parsed.healedFiles)) {
                return { success: false, healedFiles: [], error: "AI response did not return a valid list of healed files" };
            }
            const healedFilesList = [];
            for (const item of parsed.healedFiles) {
                const filePath = path.join(outputDir, item.filename);
                await fs.writeFile(filePath, item.code);
                healedFilesList.push(item.filename);
            }
            // Run orchestrator's typescript validator to confirm it builds!
            const orchestrator = new LandingPageOrchestrator_1.LandingPageOrchestrator(this.projectRoot, outputDir, false);
            // Override its manifests in state so they match what we loaded
            if (projectContext)
                orchestrator.state.projectContext = projectContext;
            if (narrative)
                orchestrator.state.narrative = narrative;
            // Run type-checking & self-healing loop
            await orchestrator.validateAndHealComponents();
            return {
                success: true,
                healedFiles: healedFilesList,
                explanation: parsed.explanation,
            };
        }
        catch (err) {
            return { success: false, healedFiles: [], error: err.message };
        }
    }
}
exports.ChatFirstController = ChatFirstController;
//# sourceMappingURL=ChatFirstController.js.map