"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;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenerateScreensCommand = void 0;
exports.registerGenerateScreensCommand = registerGenerateScreensCommand;
const chalk_1 = __importDefault(require("chalk"));
const fs = __importStar(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const fileNames_1 = require("../constants/fileNames");
const spinner_1 = require("../utils/spinner");
const logger_1 = require("../utils/logger");
const hybridAIClient_1 = require("../utils/hybridAIClient");
const child_process_1 = require("child_process");
const util_1 = require("util");
const AICore_1 = require("../core/ai/AICore");
const contextRenderer_1 = require("../utils/contextRenderer");
const BrainClient_1 = require("../core/brain/BrainClient");
const execAsync = (0, util_1.promisify)(child_process_1.exec);
/**
 * Generate visual screens (HTML/PNG) using Gemini API + nanobanana-style generation
 * Reads context from .mycontext/ and generates screens with realistic previews
 */
class GenerateScreensCommand {
    constructor() {
        this.spinner = new spinner_1.EnhancedSpinner("Generating screens...");
        this.ai = new hybridAIClient_1.HybridAIClient();
        this.brain = BrainClient_1.BrainClient.getInstance();
    }
    async execute(screenName, options = {}) {
        const projectPath = process.cwd();
        const contextDir = path_1.default.join(projectPath, ".mycontext");
        const screensDir = path_1.default.join(contextDir, "screens");
        const brain = await AICore_1.AICore.getInstance().getLivingContext();
        this.spinner.start();
        // Check if .mycontext directory exists
        if (!fs.existsSync(contextDir)) {
            this.spinner.fail("No .mycontext directory found");
            console.log(chalk_1.default.red("\n❌ No .mycontext directory found. Run 'mycontext init' first."));
            return;
        }
        // Check if AI client is available (has at least one provider)
        const providerName = await this.ai.getActiveProviderName();
        if (providerName === "unknown") {
            this.spinner.fail("No AI providers configured");
            console.log(chalk_1.default.red("\n❌ No AI providers configured. Set GITHUB_TOKEN or GEMINI_API_KEY."));
            return;
        }
        try {
            // Create screens directory if it doesn't exist
            await fs.ensureDir(screensDir);
            // Load context files
            const context = await this.loadContext(contextDir);
            // Determine which screens to generate
            const screensToGenerate = await this.determineScreens(screenName, context, options);
            this.spinner.succeed("Context loaded");
            console.log(chalk_1.default.blue(`\n🎨 Generating ${screensToGenerate.length} screen(s)...\n`));
            // Generate screens
            const generatedScreens = [];
            for (const screen of screensToGenerate) {
                const screenSpinner = new spinner_1.EnhancedSpinner(`Generating ${screen.name}...`);
                screenSpinner.start();
                try {
                    const metadata = await this.generateScreen(screen, context, screensDir, options, brain);
                    generatedScreens.push(metadata);
                    screenSpinner.succeed(`Generated ${screen.name}`);
                }
                catch (error) {
                    screenSpinner.fail(`Failed to generate ${screen.name}`);
                    logger_1.logger.error(`Screen generation error:`, error.message);
                }
            }
            // Update screens manifest
            await this.updateScreensManifest(screensDir, generatedScreens);
            // Sync with context.json (Living Brain)
            await this.syncWithLivingBrain(projectPath, generatedScreens);
            // Show summary
            console.log(chalk_1.default.green(`\n✅ Generated ${generatedScreens.length} screen(s)`));
            console.log(chalk_1.default.gray(`   Output: ${screensDir}`));
            // Show hosted Studio preview link
            console.log(chalk_1.default.blue(`\n🎨 Preview your screens at: ${chalk_1.default.bold("https://studio.mycontext.app")}`));
            console.log(chalk_1.default.gray(`   Upload your .mycontext/ directory to preview all generated screens`));
            // Auto-open in browser if requested (opens local HTML files)
            if (options.open !== false && generatedScreens.length > 0) {
                await this.openInBrowser(screensDir, generatedScreens[0]);
            }
        }
        catch (error) {
            this.spinner.fail("Screen generation failed");
            logger_1.logger.error("Error generating screens:", error.message);
            throw error;
        }
    }
    /**
     * Load context files for screen generation
     */
    async loadContext(contextDir) {
        const context = {};
        // Load PRD
        const prdPath = path_1.default.join(contextDir, fileNames_1.CONTEXT_FILES.PRD);
        if (fs.existsSync(prdPath)) {
            context.prd = fs.readFileSync(prdPath, "utf8");
        }
        // Load brand guidelines
        const brandPath = path_1.default.join(contextDir, fileNames_1.CONTEXT_FILES.BRANDING);
        if (fs.existsSync(brandPath)) {
            context.brand = fs.readFileSync(brandPath, "utf8");
        }
        // Load user flows
        const flowsPath = path_1.default.join(contextDir, fileNames_1.CONTEXT_FILES.USER_FLOWS);
        if (fs.existsSync(flowsPath)) {
            context.flows = fs.readFileSync(flowsPath, "utf8");
        }
        // Load features
        const featuresPath = path_1.default.join(contextDir, fileNames_1.CONTEXT_FILES.FEATURES);
        if (fs.existsSync(featuresPath)) {
            context.features = fs.readFileSync(featuresPath, "utf8");
        }
        // Load sample data
        const sampleDataPath = path_1.default.join(contextDir, "sample-data.json");
        if (fs.existsSync(sampleDataPath)) {
            try {
                context.sampleData = JSON.parse(fs.readFileSync(sampleDataPath, "utf8"));
            }
            catch (error) {
                logger_1.logger.warn("Could not load sample data");
            }
        }
        // Load screens from context.json if present
        const contextJsonPath = path_1.default.join(contextDir, "context.json");
        if (fs.existsSync(contextJsonPath)) {
            try {
                const contextJson = await fs.readJson(contextJsonPath);
                if (contextJson.screens) {
                    context.screens = contextJson.screens;
                }
            }
            catch (error) {
                // Not a big deal
            }
        }
        return context;
    }
    /**
     * Determine which screens to generate based on options and context
     */
    async determineScreens(screenName, context, options) {
        const screens = [];
        if (screenName) {
            // Generate specific screen
            screens.push({
                name: screenName,
                prompt: `Generate a ${screenName} screen for this application`,
                group: options.group || "custom",
            });
        }
        else if (options.all) {
            // Generate all screens from flows
            const screenNames = this.extractScreenNamesFromFlows(context.flows);
            screenNames.forEach((name) => {
                screens.push({
                    name,
                    prompt: `Generate a ${name} screen for this application`,
                    group: "flow",
                });
            });
        }
        else if (context.screens && Array.isArray(context.screens)) {
            // Use screens defined in context.json
            context.screens.forEach((s) => {
                screens.push({
                    name: s.name,
                    prompt: s.prompt || `Generate a ${s.name} screen`,
                    group: s.group || "context",
                });
            });
        }
        else {
            // Generate common screens
            screens.push({ name: "home", prompt: "Generate a home/landing page", group: "core" }, { name: "login", prompt: "Generate a login screen", group: "core" }, { name: "dashboard", prompt: "Generate a dashboard screen", group: "core" });
        }
        return screens;
    }
    /**
     * Extract screen names from user flows
     */
    extractScreenNamesFromFlows(flows) {
        if (!flows)
            return [];
        const screenNames = [];
        const lines = flows.split("\n");
        // Look for screen mentions in flows
        lines.forEach((line) => {
            // Match patterns like "1. Login Screen", "- Dashboard", etc.
            const match = line.match(/(?:^|\s)([A-Z][a-z]+(?:\s[A-Z][a-z]+)*)\s*(?:Screen|Page|View)/i);
            if (match && match[1]) {
                const name = match[1].toLowerCase().replace(/\s+/g, "-");
                if (!screenNames.includes(name)) {
                    screenNames.push(name);
                }
            }
        });
        return screenNames.length > 0 ? screenNames : ["home", "dashboard"];
    }
    /**
     * Generate a single screen
     */
    async generateScreen(screen, context, screensDir, options, brain) {
        const includeScreenshot = options.format?.includes("png") || options.includeScreenshot || false;
        const format = options.format || "html";
        // Load images manifest if it exists
        let imagesContext = "";
        const manifestPath = path_1.default.join(process.cwd(), ".mycontext", "images-manifest.json");
        let imagesManifest = null;
        if (fs.existsSync(manifestPath)) {
            try {
                imagesManifest = await fs.readJson(manifestPath);
                const assets = Array.isArray(imagesManifest.assets) ? imagesManifest.assets : (imagesManifest.visualAssets || imagesManifest);
                if (Array.isArray(assets)) {
                    imagesContext = "\nLOCAL ASSETS AVAILABLE (Use these paths instead of placeholders):\n";
                    assets.forEach((a) => {
                        imagesContext += `- ${a.id}: /assets/images/${a.id}.png (${a.description})\n`;
                    });
                }
            }
            catch (err) {
                logger_1.logger.warn("Could not load images manifest");
            }
        }
        // Build prompt based on format
        let finalPrompt = screen.prompt;
        // Global Design System Context
        const designSystem = `
      ## GLOBAL DESIGN SYSTEM (PREMIUM STANDARDS):
      - Typography: Use modern sans-serif fonts (e.g., Inter, Outfit, System-UI).
      - Color Palette: Deep rich colors, vibrant accents, sleek dark modes.
      - Contrast: Ensure high contrast for text visibility. AVOID white-on-light or black-on-dark issues.
      - Aesthetics: Use Glassmorphism (backdrop-blur), soft shadows, and subtle gradients.
      - Spacing: Expertly balanced whitespace and grid systems.
    `;
        // Standardized Layout Context
        const layoutContext = `
      ## STANDARDIZED LAYOUT REQUIREMENTS:
      - All screens MUST be wrapped in a consistent 'Layout' structure.
      - HEADER: Include a professional logo, navigation links, and a functional-looking 'ThemeToggle' (Light/Dark).
      - FOOTER: Include company info, links, social icons, and copyright.
      - Ensure the layout is responsive and works world-class on mobile and desktop.
    `;
        finalPrompt = `${designSystem}\n${layoutContext}\n\n## SCREEN SPECIFIC REQUEST:\n${finalPrompt}`;
        if (imagesContext) {
            finalPrompt += `\n\nCRITICAL: ${imagesContext}\n\nSTRICT REQUIREMENT: You MUST use the local asset paths provided above for ALL images in this screen. DO NOT use Unsplash, Placehold.it, or any other external image services. If a local asset matches the intended use (e.g., 'hero-background' for a hero section), YOU MUST USE IT. For the hero background, use: \`<body style="background-image: url('/assets/images/hero-background.png'); background-size: cover;">\` or a similar high-impact implementation.`;
        }
        if (format === "jsx") {
            finalPrompt += ". Use React, Tailwind CSS, and Lucide/Radix components. Export as a default functional component.";
        }
        // Generate visual screen using HybridAIClient
        let result;
        const enrichedContext = { ...context, imagesManifest };
        if (format === "html") {
            // For HTML, we can still use Gemini if available for best visual results, or fallback to generic text
            const provider = await this.ai.getActiveProviderName();
            if (provider === "gemini") {
                const gemini = this.ai.providers.find((p) => p.name === "gemini").client;
                result = await gemini.generateVisualScreen(finalPrompt, enrichedContext, { includeScreenshot });
            }
            else {
                const response = await this.ai.generateText(this.buildVisualHtmlPrompt(finalPrompt, enrichedContext, brain), options);
                result = {
                    html: this.extractCodeBlock(response.text, "html"),
                    metadata: { model: response.provider, timestamp: new Date().toISOString() }
                };
            }
        }
        else {
            // JSX format
            const response = await this.ai.generateText(this.buildVisualJsxPrompt(finalPrompt, context, brain), options);
            result = {
                html: this.extractCodeBlock(response.text, "tsx") || this.extractCodeBlock(response.text, "jsx") || response.text,
                metadata: { model: response.provider, timestamp: new Date().toISOString() }
            };
        }
        // Create screen directory
        const screenDir = path_1.default.join(screensDir, screen.name);
        await fs.ensureDir(screenDir);
        // Save File
        const fileName = format === "jsx" ? "index.tsx" : "index.html";
        const filePath = path_1.default.join(screenDir, fileName);
        await fs.writeFile(filePath, result.html, "utf8");
        // Save screenshot if generated
        if (result.screenshot) {
            const pngPath = path_1.default.join(screenDir, "preview.png");
            const buffer = Buffer.from(result.screenshot, "base64");
            await fs.writeFile(pngPath, buffer);
        }
        // Save generation metadata
        const metadataPath = path_1.default.join(screenDir, "metadata.json");
        const metadata = {
            name: screen.name,
            prompt: screen.prompt,
            model: result.metadata.model,
            timestamp: result.metadata.timestamp,
            format: [format, result.screenshot ? "png" : null].filter(Boolean),
            path: screenDir,
            group: screen.group,
        };
        await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), "utf8");
        // Sync with Brain
        await this.brain.addUpdate("ScreenGenerator", "builder", "action", `Generated ${format.toUpperCase()} screen for '${screen.name}' in group '${screen.group || 'default'}'.`, { path: filePath, model: result.metadata.model });
        await this.brain.updateArtifact("code", result.html, filePath);
        return metadata;
    }
    buildVisualHtmlPrompt(userPrompt, context, brain) {
        const prd = brain ? contextRenderer_1.ContextRenderer.renderPRD(brain) : context.prd;
        const brand = brain ? contextRenderer_1.ContextRenderer.renderBrandGuide(brain) : context.brand;
        const features = brain ? contextRenderer_1.ContextRenderer.renderFeatures(brain) : context.features;
        const aesthetic = brain?.aestheticPreference || "modern";
        const projectType = brain?.projectType || "application";
        return `You are an elite UI/UX engineer building a ${aesthetic} ${projectType}.
    
USER INSTRUCTION: ${userPrompt}

FULL PROJECT CONTEXT (Living Brain):
${prd}

DESIGN SYSTEM & BRANDING:
${brand}

CORE FEATURES TO HIGHLIGHT:
${features}

REQUIREMENTS:
1. Use BEAUTIFUL, high-quality Tailwind CSS.
2. Incorporate the specific aesthetic: ${aesthetic}.
3. NO generic placeholders like "Application Name". Use the real project name: ${brain?.metadata?.projectConfig?.name || "The App"}.
4. Use realistic, industry-specific copy and imagery (via Unsplash/Placehold.co).
5. Ensure the layout is premium and follows modern web design trends (e.g., glassmorphism, dynamic gradients, perfect spacing).

Return ONLY the HTML code wrapped in \`\`\`html blocks.`;
    }
    buildVisualJsxPrompt(userPrompt, context, brain) {
        const prd = brain ? contextRenderer_1.ContextRenderer.renderPRD(brain) : context.prd;
        const brand = brain ? contextRenderer_1.ContextRenderer.renderBrandGuide(brain) : context.brand;
        const aesthetic = brain?.aestheticPreference || "modern";
        return `You are an elite Full-Stack UI Architect. Generate a production-ready, highly-polished React component (TSX) using Tailwind CSS.

USER INSTRUCTION: ${userPrompt}

AESTHETIC GOAL: ${aesthetic} (High-Fidelity, Premium)

BRAIN CONTEXT:
${prd}

BRANDING DATA:
${brand}

TECHNICAL REQUIREMENTS:
- Use React (Functional Components)
- Use Tailwind CSS for all styling (Premium patterns)
- Use Lucide-React for icons
- Implementation MUST BE high-fidelity - avoid "dull" or "generic" layouts.
- Use the actual Project Name: ${brain?.metadata?.projectConfig?.name || "The App"}.

CRITICAL: Return the COMPLETE file content. DO NOT TRUNCATE. Ensure all components (Layout, Header, Hero, Features, Footer) are fully implemented in the same file.

Return ONLY the TSX code wrapped in \`\`\`tsx blocks.`;
    }
    extractCodeBlock(content, lang) {
        const pattern = new RegExp(`\`\`\`${lang}\\n([\\s\\S]*?)\\n\`\`\``);
        const match = content.match(pattern);
        return match && match[1] ? match[1].trim() : content;
    }
    /**
     * Update screens manifest file
     */
    async updateScreensManifest(screensDir, screens) {
        const manifestPath = path_1.default.join(screensDir, "screens-manifest.json");
        // Load existing manifest if it exists
        let manifest = {
            screens: [],
            lastUpdated: new Date().toISOString(),
        };
        if (fs.existsSync(manifestPath)) {
            try {
                manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
            }
            catch (error) {
                logger_1.logger.warn("Could not load existing manifest");
            }
        }
        // Update manifest with new screens
        screens.forEach((newScreen) => {
            const existingIndex = manifest.screens.findIndex((s) => s.name === newScreen.name);
            if (existingIndex >= 0) {
                manifest.screens[existingIndex] = newScreen;
            }
            else {
                manifest.screens.push(newScreen);
            }
        });
        manifest.lastUpdated = new Date().toISOString();
        // Save manifest
        await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
    }
    /**
     * Open generated screen in browser
     */
    async openInBrowser(screensDir, screen) {
        if (!screen)
            return;
        const fileName = screen.format.includes("jsx") ? "index.tsx" : "index.html";
        const filePath = path_1.default.join(screen.path, fileName);
        if (!fs.existsSync(filePath)) {
            logger_1.logger.warn("HTML/JSX file not found, cannot open in browser");
            return;
        }
        try {
            console.log(chalk_1.default.blue("\n🌐 Opening screen in browser..."));
            // Platform-specific open command
            const command = process.platform === "darwin"
                ? "open"
                : process.platform === "win32"
                    ? "start"
                    : "xdg-open";
            await execAsync(`${command} "${filePath}"`);
        }
        catch (error) {
            logger_1.logger.warn("Could not auto-open browser:", error);
            console.log(chalk_1.default.gray(`\n💡 Manually open: ${filePath}`));
        }
    }
    /**
     * Sync generated screens back to the Living Brain (context.json)
     */
    async syncWithLivingBrain(projectPath, screens) {
        const contextPath = path_1.default.join(projectPath, ".mycontext", "context.json");
        if (!fs.existsSync(contextPath))
            return;
        try {
            const context = await fs.readJson(contextPath);
            if (!context.screens)
                context.screens = [];
            screens.forEach(screen => {
                const existingIndex = context.screens.findIndex((s) => s.name === screen.name);
                if (existingIndex >= 0) {
                    context.screens[existingIndex] = screen;
                }
                else {
                    context.screens.push(screen);
                }
            });
            if (!context.metadata)
                context.metadata = {};
            context.metadata.lastUpdatedAt = new Date().toISOString();
            await fs.writeJson(contextPath, context, { spaces: 2 });
            console.log(chalk_1.default.gray("   Brain synchronized with new screens."));
        }
        catch (error) {
            logger_1.logger.error("Failed to sync with Living Brain:", error);
        }
    }
}
exports.GenerateScreensCommand = GenerateScreensCommand;
/**
 * Register the generate:screens command
 */
function registerGenerateScreensCommand(program) {
    program
        .command("generate:screens [screen-name]")
        .alias("gs")
        .description("Generate visual screens (HTML/JSX) - Preview at https://studio.mycontext.app")
        .option("-a, --all", "Generate all screens from user flows")
        .option("-f, --format <formats>", "Output formats: html, png, jsx, or both", "html")
        .option("-g, --group <name>", "Group name for the screen")
        .option("-o, --output <path>", "Output directory (default: .mycontext/screens)")
        .option("-s, --screenshot", "Include PNG screenshots (requires puppeteer)")
        .option("--no-open", "Don't auto-open in browser")
        .option("-v, --verbose", "Show detailed output")
        .action(async (screenName, options) => {
        const command = new GenerateScreensCommand();
        await command.execute(screenName, {
            ...options,
            includeScreenshot: options.screenshot || options.format?.includes("png"),
        });
    });
}
//# sourceMappingURL=generate-screens.js.map