/**
 * EditorPane
 *
 * VSCode-style editor surface for the IDE tab: a tab strip at the top
 * showing open files (with dirty indicators + close buttons) and a
 * Monaco editor below it. Monaco is dynamically imported so it stays
 * out of the initial bundle and never runs during SSR.
 */
'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useCallback, useEffect, useMemo } from 'react';
import dynamic from 'next/dynamic';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Eye, FileCode, File as FileIcon, PanelRight, X } from 'lucide-react';
import { cn } from '../../../../lib/utils.js';
import { ImageViewer } from './image-viewer.js';
import { useResolvedTheme } from './use-resolved-theme.js';
/** Monaco is a large client-only dependency — dynamic import with ssr:false. */
const MonacoEditor = dynamic(() => import('@monaco-editor/react'), {
    ssr: false,
    loading: () => (_jsx("div", { className: "text-muted-foreground flex h-full items-center justify-center text-xs", children: "Loading editor\u2026" })),
});
const EXTENSION_TO_LANGUAGE = {
    ts: 'typescript',
    tsx: 'typescript',
    js: 'javascript',
    jsx: 'javascript',
    mjs: 'javascript',
    cjs: 'javascript',
    json: 'json',
    md: 'markdown',
    mdx: 'markdown',
    css: 'css',
    scss: 'scss',
    html: 'html',
    yml: 'yaml',
    yaml: 'yaml',
    toml: 'ini',
    ini: 'ini',
    sh: 'shell',
    bash: 'shell',
    py: 'python',
    go: 'go',
    rs: 'rust',
    java: 'java',
    sql: 'sql',
    xml: 'xml',
    dockerfile: 'dockerfile',
};
function languageForPath(path) {
    const base = path.split('/').pop() ?? path;
    if (base.toLowerCase() === 'dockerfile')
        return 'dockerfile';
    const ext = base.includes('.') ? base.split('.').pop()?.toLowerCase() : '';
    return (ext ? EXTENSION_TO_LANGUAGE[ext] : undefined) ?? 'plaintext';
}
function basename(p) {
    const i = p.lastIndexOf('/');
    return i === -1 ? p : p.slice(i + 1);
}
export function EditorPane({ applicationId, openFiles, activePath, onSelect, onClose, onChange, onSave, onPromote, onToggleViewMode, sidebarCollapsed, onShowSidebar, }) {
    const active = useMemo(() => openFiles.find((f) => f.path === activePath) ?? null, [openFiles, activePath]);
    const resolvedTheme = useResolvedTheme();
    const isDark = resolvedTheme === 'dark';
    const monacoTheme = isDark ? 'vs-dark' : 'vs';
    // Background colour that matches each Monaco theme exactly, so the brief
    // remount gap when switching files never flashes an off-tone fill.
    const editorBgClass = isDark ? 'bg-[#1e1e1e]' : 'bg-[#ffffff]';
    // Ctrl/Cmd+S saves the active file.
    useEffect(() => {
        const handler = (e) => {
            if ((e.ctrlKey || e.metaKey) && (e.key === 's' || e.key === 'S')) {
                e.preventDefault();
                onSave();
            }
        };
        window.addEventListener('keydown', handler);
        return () => window.removeEventListener('keydown', handler);
    }, [onSave]);
    const handleChange = useCallback((value) => {
        if (!active)
            return;
        onChange(active.path, value ?? '');
    }, [active, onChange]);
    // Monaco's in-browser TS language service has no project graph (no
    // tsconfig, no node_modules), so every `import` in a .ts/.tsx file would
    // otherwise render as a red "Cannot find module" squiggle. Disable
    // semantic validation — we still surface real syntax errors — so the
    // preview matches what the file actually looks like on disk.
    const handleBeforeMount = useCallback((monaco) => {
        const ts = monaco.languages.typescript;
        const compilerOptions = {
            target: ts.ScriptTarget.Latest,
            module: ts.ModuleKind.ESNext,
            moduleResolution: ts.ModuleResolutionKind.NodeJs,
            jsx: ts.JsxEmit.Preserve,
            allowJs: true,
            allowNonTsExtensions: true,
            esModuleInterop: true,
            isolatedModules: true,
            noEmit: true,
            skipLibCheck: true,
        };
        ts.typescriptDefaults.setCompilerOptions(compilerOptions);
        ts.javascriptDefaults.setCompilerOptions(compilerOptions);
        const diagnostics = {
            noSemanticValidation: true,
            noSyntaxValidation: false,
        };
        ts.typescriptDefaults.setDiagnosticsOptions(diagnostics);
        ts.javascriptDefaults.setDiagnosticsOptions(diagnostics);
    }, []);
    return (_jsxs("div", { className: "flex h-full min-h-0 flex-col", children: [_jsxs("div", { role: "tablist", "aria-label": "Open files", className: "border-border bg-muted/30 flex h-8 shrink-0 items-center border-b", children: [_jsx("div", { className: "flex min-w-0 flex-1 items-center overflow-x-auto", children: openFiles.length === 0 ? (_jsx("div", { className: "text-muted-foreground px-3 text-[11px]", children: "No file open" })) : (openFiles.map((f) => {
                            const dirty = f.content !== f.originalContent;
                            const selected = f.path === activePath;
                            return (_jsxs("div", { role: "tab", "aria-selected": selected, onClick: () => onSelect(f.path), onDoubleClick: () => onPromote(f.path), className: cn('group border-border flex h-8 shrink-0 cursor-pointer items-center gap-1.5 border-r px-3 text-[11px]', selected
                                    ? 'bg-background text-foreground'
                                    : 'text-muted-foreground hover:bg-muted/60'), title: f.isPreview
                                    ? `${f.path} — preview (double-click tab or edit to keep open)`
                                    : f.path, children: [_jsx(FileIcon, { className: "h-3 w-3 shrink-0 text-sky-500" }), _jsx("span", { className: cn('truncate', f.isPreview && 'italic'), children: basename(f.path) }), dirty ? (_jsx("span", { className: "bg-primary/70 inline-block h-1.5 w-1.5 rounded-full", "aria-label": "Unsaved changes" })) : null, _jsx("button", { type: "button", onClick: (e) => {
                                            e.stopPropagation();
                                            onClose(f.path);
                                        }, className: "text-muted-foreground/70 hover:text-foreground ml-1 rounded-sm p-0.5", "aria-label": `Close ${f.path}`, children: _jsx(X, { className: "h-3 w-3" }) })] }, f.path));
                        })) }), active?.isMarkdown ? (_jsx("button", { type: "button", onClick: () => onToggleViewMode(active.path), className: "text-muted-foreground hover:text-foreground border-border flex h-8 w-8 shrink-0 items-center justify-center border-l", "aria-label": active.viewMode === 'rendered' ? 'Show markdown source' : 'Show markdown preview', title: active.viewMode === 'rendered' ? 'Show markdown source' : 'Show markdown preview', children: active.viewMode === 'rendered' ? (_jsx(FileCode, { className: "h-3.5 w-3.5" })) : (_jsx(Eye, { className: "h-3.5 w-3.5" })) })) : null, sidebarCollapsed ? (_jsx("button", { type: "button", onClick: onShowSidebar, className: "text-muted-foreground hover:text-foreground border-border flex h-8 w-8 shrink-0 items-center justify-center border-l", "aria-label": "Show file explorer", title: "Show file explorer", children: _jsx(PanelRight, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("div", { className: cn('min-h-0 flex-1', editorBgClass), children: active ? (active.isImage ? (_jsx(ImageViewer, { applicationId: applicationId, path: active.path })) : active.binary ? (_jsx(EmptyMessage, { children: "Binary file \u2014 preview not available" })) : active.tooLarge ? (_jsx(EmptyMessage, { children: "File is too large to preview" })) : active.isMarkdown && active.viewMode === 'rendered' ? (_jsx(MarkdownPreview, { source: active.content, isDark: isDark })) : (_jsx(MonacoEditor, { height: "100%", theme: monacoTheme, language: languageForPath(active.path), value: active.content, onChange: handleChange, beforeMount: handleBeforeMount, options: {
                        fontSize: 13,
                        minimap: { enabled: true },
                        scrollBeyondLastLine: false,
                        automaticLayout: true,
                        tabSize: 2,
                        wordWrap: 'off',
                        renderWhitespace: 'selection',
                        smoothScrolling: true,
                    } }, active.path))) : (_jsx(EmptyMessage, { children: "Select a file from the tree to start editing" })) })] }));
}
function EmptyMessage({ children }) {
    return (_jsx("div", { className: "text-muted-foreground flex h-full items-center justify-center text-xs", children: children }));
}
/**
 * MarkdownPreview
 *
 * Lightweight rendered view for `.md` / `.mdx` files using `react-markdown`
 * + `remark-gfm` (tables, strikethrough, task lists). Styled against the
 * vs-dark editor backdrop so it doesn't clash when toggling source/preview.
 */
/**
 * Stable (module-scoped) `Components` map for the markdown preview.
 *
 * Styling is expressed with Tailwind's `dark:` variants and semantic
 * tokens (`border-border`, `text-foreground`) so the preview follows the
 * app theme without us reading `isDark` in the render tree.
 */
const MARKDOWN_COMPONENTS = {
    h1: ({ children }) => (_jsx("h1", { className: "border-border text-foreground mt-6 mb-4 border-b pb-2 text-2xl font-semibold", children: children })),
    h2: ({ children }) => (_jsx("h2", { className: "border-border text-foreground mt-5 mb-3 border-b pb-1 text-xl font-semibold", children: children })),
    h3: ({ children }) => (_jsx("h3", { className: "text-foreground mt-4 mb-2 text-lg font-semibold", children: children })),
    p: ({ children }) => _jsx("p", { className: "my-3", children: children }),
    a: ({ children, href }) => (_jsx("a", { href: href, target: "_blank", rel: "noreferrer", className: "text-sky-600 underline hover:text-sky-500 dark:text-sky-400 dark:hover:text-sky-300", children: children })),
    ul: ({ children }) => _jsx("ul", { className: "my-3 ml-6 list-disc", children: children }),
    ol: ({ children }) => _jsx("ol", { className: "my-3 ml-6 list-decimal", children: children }),
    li: ({ children }) => _jsx("li", { className: "my-1", children: children }),
    blockquote: ({ children }) => (_jsx("blockquote", { className: "border-border text-muted-foreground my-3 border-l-4 pl-4 italic", children: children })),
    code: ({ className, children }) => {
        const inline = !className;
        return inline ? (_jsx("code", { className: "bg-muted rounded px-1.5 py-0.5 font-mono text-[12px] text-pink-600 dark:text-pink-300", children: children })) : (_jsx("code", { className: cn('font-mono text-[12px]', className), children: children }));
    },
    pre: ({ children }) => (_jsx("pre", { className: "border-border bg-muted/60 my-4 overflow-auto rounded-md border p-3", children: children })),
    hr: () => _jsx("hr", { className: "border-border my-6" }),
    table: ({ children }) => (_jsx("table", { className: "border-border my-4 border-collapse border", children: children })),
    th: ({ children }) => (_jsx("th", { className: "border-border bg-muted/60 border px-3 py-1 text-left font-semibold", children: children })),
    td: ({ children }) => _jsx("td", { className: "border-border border px-3 py-1", children: children }),
};
function MarkdownPreview({ source, isDark }) {
    return (_jsx("div", { className: cn('text-foreground h-full overflow-auto px-8 py-6 text-[13px]', isDark ? 'bg-[#1e1e1e]' : 'bg-[#ffffff]'), children: _jsx("div", { className: "markdown-preview mx-auto max-w-3xl leading-6", children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: MARKDOWN_COMPONENTS, children: source }) }) }));
}
