import projectModeCatalogData from '../project-mode-catalog.json' with { type: 'json' };
export type ProjectSurface = 'quick-code' | 'playground' | 'full-project';
export type ProjectType = 'web' | 'code' | 'document';
export type ProjectOutputKind = 'web-preview' | 'console' | 'pdf-preview' | 'visual-editor';
export type ProjectFrameworkId = 'react-vite' | 'vite' | 'vue-vite' | 'svelte-vite' | 'node' | 'vanilla-web';
export type ProjectModeId =
  | 'latex' | 'html' | 'javascript' | 'typescript' | 'node-javascript' | 'node-typescript'
  | 'python' | 'c' | 'cpp' | 'java' | 'go' | 'rust' | 'php' | 'ruby' | 'r' | 'shell' | 'markdown' | 'interactive-doc'
  | 'typescript-vite' | 'react-vite-javascript' | 'react-vite-typescript' | 'vue-vite' | 'svelte-vite';
export type ProjectOperation = 'RUN' | 'BUILD' | 'TEST' | 'PREVIEW' | 'EXPORT';
export type ProjectProvider =
  | 'python' | 'node' | 'typescript' | 'static-web' | 'vite' | 'next' | 'express'
  | 'flask' | 'fastapi' | 'django' | 'c' | 'cpp' | 'make' | 'cmake' | 'rust'
  | 'go' | 'java' | 'maven' | 'gradle' | 'php' | 'ruby' | 'r' | 'shell'
  | 'markdown' | 'latex' | 'custom';

export type ProjectLanguageId =
  | 'javascript' | 'jsx' | 'typescript' | 'tsx'
  | 'html' | 'css' | 'scss' | 'json'
  | 'python' | 'c' | 'cpp' | 'java' | 'markdown' | 'php' | 'shell'
  | 'yaml' | 'sql' | 'rust' | 'go' | 'r' | 'ruby' | 'toml' | 'latex' | 'plain';

export interface ProjectLanguageDefinition {
  id: ProjectLanguageId;
  label: string;
  iconText: string;
  extensions: readonly string[];
  popular: boolean;
  defaultStarterId?: string;
  starterIds: readonly string[];
  category: 'programming' | 'web' | 'documents' | 'data' | 'other';
  resultMode: 'console' | 'preview' | 'pdf' | 'none';
  executable: boolean;
}

export interface ProjectProfile {
  version: 1;
  modeId?: ProjectModeId;
  projectType: string;
  language?: string;
  runtime?: string;
  entry?: string;
  providers: Partial<Record<ProjectOperation, ProjectProvider>>;
  commands?: Partial<Record<'run' | 'dev' | 'build' | 'test' | 'export', string>>;
  preview?: { port: 'auto' | number };
}

export interface ProjectCapabilities {
  projectSearch: boolean;
  diagnostics: boolean;
  fileRefactor: boolean;
  latexTools: boolean;
  paperMemory: boolean;
  latexAiActions: boolean;
}


export interface ProjectModeDefinition {
  id: ProjectModeId;
  label: string;
  catalogLabel?: string;
  projectType: ProjectType;
  primaryLanguageId: ProjectLanguageId;
  primaryLanguage?: string;
  frameworkId?: ProjectFrameworkId;
  framework?: string | null;
  starterId: string;
  defaultEntryFile: string;
  namingPrefix: string;
  defaultOutput: ProjectOutputKind;
  supportsConsole: boolean;
  supportsStdin: boolean;
  supportsWebPreview: boolean;
  supportsPdfPreview: boolean;
  buildOutput?: string;
  popular: boolean;
  runtime?: { provider: ProjectProvider; operation: ProjectOperation };
}

export interface ProjectStarter {
  id: string;
  label: string;
  surface: ProjectSurface;
  category: 'programming' | 'web' | 'documents' | 'build-systems' | 'other';
  profile: ProjectProfile;
  files: Readonly<Record<string, string>>;
}

export interface ProjectTarget {
  id: string;
  root: string;
  label: string;
  profile: ProjectProfile;
  confidence: number;
  reason: string;
}

export interface ExecutionDiagnostic {
  message: string;
  severity: 'error' | 'warning' | 'info';
  file?: string;
  line?: number;
  column?: number;
}

export interface ExecutionArtifact {
  path: string;
  kind?: string;
  mediaType?: string;
  size?: number;
  contentBase64?: string;
}

export interface PreviewTarget {
  kind: 'http' | 'pdf' | 'markdown';
  port?: number;
  path?: string;
}

export interface UniversalExecutionResult {
  executionId?: string;
  status: 'queued' | 'running' | 'success' | 'error' | 'cancelled' | 'timeout';
  operation: ProjectOperation;
  provider: ProjectProvider;
  exitCode: number | null;
  stdout: string;
  stderr: string;
  diagnostics: ExecutionDiagnostic[];
  duration: number;
  artifacts: ExecutionArtifact[];
  previewTargets: PreviewTarget[];
  testResults: Array<{ name: string; status: 'passed' | 'failed' | 'skipped'; duration?: number; message?: string }>;
}


export function isProjectProfile(value: unknown): value is ProjectProfile {
  if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
  const candidate = value as Record<string, unknown>;
  if (candidate.version !== 1 || typeof candidate.projectType !== 'string' || !candidate.projectType.trim()) return false;
  if (!candidate.providers || typeof candidate.providers !== 'object' || Array.isArray(candidate.providers)) return false;
  const allowedProviders = new Set<ProjectProvider>(['python','node','typescript','static-web','vite','next','express','flask','fastapi','django','c','cpp','make','cmake','rust','go','java','maven','gradle','php','ruby','r','shell','markdown','latex','custom']);
  for (const [operation, provider] of Object.entries(candidate.providers as Record<string, unknown>)) {
    if (!['RUN','BUILD','TEST','PREVIEW','EXPORT'].includes(operation) || typeof provider !== 'string' || !allowedProviders.has(provider as ProjectProvider)) return false;
  }
  if (candidate.entry !== undefined) { try { safeRelativeConfigPath(String(candidate.entry)); } catch { return false; } }
  return true;
}

export const GENERATED_DIRECTORY_EXCLUSIONS = Object.freeze([
  'node_modules', 'dist', 'build', '.next', 'target', '.cache', '__pycache__', '.venv', 'venv', '.git', '.learnitive-tmp',
]);

function profile(projectType: string, language: string | undefined, runtime: string | undefined, entry: string | undefined, providers: ProjectProfile['providers'], commands?: ProjectProfile['commands'], preview?: ProjectProfile['preview'], modeId?: ProjectModeId): ProjectProfile {
  return { version: 1, ...(modeId ? { modeId } : {}), projectType, ...(language ? { language } : {}), ...(runtime ? { runtime } : {}), ...(entry ? { entry } : {}), providers, ...(commands ? { commands } : {}), ...(preview ? { preview } : {}) };
}

function starter(id: string, label: string, surface: ProjectSurface, category: ProjectStarter['category'], p: ProjectProfile, files: Record<string, string>): ProjectStarter {
  return { id, label, surface, category, profile: p, files };
}

function jsonSource(value: unknown): string {
  return `${JSON.stringify(value, null, 2)}\n`;
}

function interactiveDocSource(blocks: Array<{ id: string; type: string; data: Record<string, unknown> }>): string {
  return jsonSource({ schemaVersion: 1, time: 0, blocks });
}

const interactiveDocProfile = () => profile('interactive-doc','Interactive Content','Interactive Content','document.interactive.json',{},undefined,undefined,'interactive-doc');

const py = 'print("Hello from Learnitive")\n';
const js = 'console.log("Hello from Learnitive");\n';
const html = '<!doctype html>\n<html lang="en">\n  <head>\n    <meta charset="UTF-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n    <title>Learnitive</title>\n    <link rel="stylesheet" href="style.css" />\n  </head>\n  <body>\n    <main>\n      <h1>Hello from Learnitive</h1>\n      <p>Edit the files and refresh Web Preview.</p>\n    </main>\n    <script src="script.js"></script>\n  </body>\n</html>\n';

export const PROJECT_STARTERS: readonly ProjectStarter[] = Object.freeze([
  starter('python', 'Python', 'quick-code', 'programming', profile('python','Python','Python','main.py',{RUN:'python',TEST:'python'},undefined,undefined,'python'), {'main.py':py}),
  starter('javascript', 'JavaScript', 'quick-code', 'programming', profile('javascript','JavaScript','Node.js','main.js',{RUN:'node'},undefined,undefined,'javascript'), {'main.js':js}),
  starter('typescript', 'TypeScript', 'quick-code', 'programming', profile('typescript','TypeScript','Node.js','main.ts',{RUN:'typescript'},undefined,undefined,'typescript'), {'main.ts':'const message: string = "Hello from Learnitive";\nconsole.log(message);\n'}),
  starter('c', 'C', 'quick-code', 'programming', profile('c','C','GCC','main.c',{RUN:'c'},undefined,undefined,'c'), {'main.c':'#include <stdio.h>\n\nint main(void) {\n  puts("Hello from Learnitive");\n  return 0;\n}\n'}),
  starter('cpp', 'C++', 'quick-code', 'programming', profile('cpp','C++','G++','main.cpp',{RUN:'cpp'},undefined,undefined,'cpp'), {'main.cpp':'#include <iostream>\n\nint main() {\n  std::cout << "Hello from Learnitive\\n";\n  return 0;\n}\n'}),
  starter('java', 'Java', 'quick-code', 'programming', profile('java','Java','OpenJDK','Main.java',{RUN:'java'},undefined,undefined,'java'), {'Main.java':'public class Main {\n  public static void main(String[] args) {\n    System.out.println("Hello from Learnitive");\n  }\n}\n'}),
  starter('go', 'Go', 'quick-code', 'programming', profile('go','Go','Go','main.go',{RUN:'go',TEST:'go'},undefined,undefined,'go'), {'main.go':'package main\n\nimport "fmt"\n\nfunc main() {\n\tfmt.Println("Hello from Learnitive")\n}\n'}),
  starter('rust', 'Rust', 'quick-code', 'programming', profile('rust','Rust','Rust','main.rs',{RUN:'rust'},undefined,undefined,'rust'), {'main.rs':'fn main() {\n    println!("Hello from Learnitive");\n}\n'}),
  starter('php', 'PHP', 'quick-code', 'programming', profile('php','PHP','PHP','main.php',{RUN:'php'},undefined,undefined,'php'), {'main.php':'<?php\n\necho "Hello from Learnitive\\n";\n'}),
  starter('ruby', 'Ruby', 'quick-code', 'programming', profile('ruby','Ruby','Ruby','main.rb',{RUN:'ruby'},undefined,undefined,'ruby'), {'main.rb':'puts "Hello from Learnitive"\n'}),
  starter('r', 'R', 'quick-code', 'programming', profile('r','R','R','main.R',{RUN:'r'},undefined,undefined,'r'), {'main.R':'cat("Hello from Learnitive\\n")\n'}),
  starter('shell', 'Bash', 'quick-code', 'programming', profile('shell','Bash','Bash','main.sh',{RUN:'shell'},undefined,undefined,'shell'), {'main.sh':'#!/usr/bin/env bash\nset -euo pipefail\necho "Hello from Learnitive"\n'}),

  starter('html-css-js', 'HTML / CSS / JS', 'playground', 'web', profile('static-web','HTML/CSS/JS','Browser','index.html',{PREVIEW:'static-web'},undefined,{port:'auto'},'html'), {'index.html':html,'style.css':'body {\n  margin: 0;\n  padding: 2rem;\n  font-family: system-ui, sans-serif;\n}\n\nmain {\n  max-width: 48rem;\n  margin: 0 auto;\n}\n','script.js':'const heading = document.querySelector("h1");\n\nif (heading) {\n  heading.addEventListener("click", () => {\n    heading.textContent = "Learnitive is interactive!";\n  });\n}\n'}),
  starter('react-vite', 'React + Vite (TypeScript)', 'playground', 'web', profile('react-vite','TypeScript','Node.js','src/main.tsx',{BUILD:'vite',PREVIEW:'vite'},undefined,{port:'auto'},'react-vite-typescript'), {
    'package.json':jsonSource({ scripts:{ build:'vite build' }, dependencies:{ vite:'8.2.1', typescript:'6.0.3', react:'19.2.7', 'react-dom':'19.2.7' } }),
    'index.html':'<!doctype html>\n<html lang="en">\n  <head>\n    <meta charset="UTF-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n    <title>React · Learnitive</title>\n  </head>\n  <body>\n    <div id="root"></div>\n    <script type="module" src="/src/main.tsx"></script>\n  </body>\n</html>\n',
    'src/main.tsx':'import React from "react";\nimport { createRoot } from "react-dom/client";\nimport App from "./App";\n\nconst root = document.getElementById("root");\n\nif (root) {\n  createRoot(root).render(<App />);\n}\n',
    'src/App.tsx':'export default function App() {\n  return (\n    <main>\n      <h1>Learnitive</h1>\n      <p>React + TypeScript + Vite is ready.</p>\n    </main>\n  );\n}\n',
  }),
  starter('react-vite-js', 'React + Vite (JavaScript)', 'playground', 'web', profile('react-vite','JavaScript','Node.js','src/main.jsx',{BUILD:'vite',PREVIEW:'vite'},undefined,{port:'auto'},'react-vite-javascript'), {
    'package.json':jsonSource({ scripts:{ build:'vite build' }, dependencies:{ vite:'8.2.1', react:'19.2.7', 'react-dom':'19.2.7' } }),
    'index.html':'<!doctype html>\n<html lang="en">\n  <head>\n    <meta charset="UTF-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n    <title>React · Learnitive</title>\n  </head>\n  <body>\n    <div id="root"></div>\n    <script type="module" src="/src/main.jsx"></script>\n  </body>\n</html>\n',
    'src/main.jsx':'import React from "react";\nimport { createRoot } from "react-dom/client";\nimport App from "./App";\n\nconst root = document.getElementById("root");\n\nif (root) {\n  createRoot(root).render(<App />);\n}\n',
    'src/App.jsx':'export default function App() {\n  return (\n    <main>\n      <h1>Learnitive</h1>\n      <p>React + JavaScript + Vite is ready.</p>\n    </main>\n  );\n}\n',
  }),
  starter('typescript-vite', 'TypeScript + Vite', 'playground', 'web', profile('typescript-vite','TypeScript','Node.js','src/main.ts',{BUILD:'vite',PREVIEW:'vite'},undefined,{port:'auto'},'typescript-vite'), {
    'package.json':jsonSource({ scripts:{ build:'vite build' }, devDependencies:{ vite:'8.2.1', typescript:'6.0.3' } }),
    'index.html':'<!doctype html>\n<html lang="en">\n  <head>\n    <meta charset="UTF-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n    <title>TypeScript · Learnitive</title>\n  </head>\n  <body>\n    <div id="app"></div>\n    <script type="module" src="/src/main.ts"></script>\n  </body>\n</html>\n',
    'src/main.ts':'import "./style.css";\n\nconst app = document.querySelector<HTMLDivElement>("#app");\n\nif (app) {\n  app.innerHTML = `\n    <main>\n      <h1>TypeScript + Vite</h1>\n      <p>Trusted build and Web Preview are ready.</p>\n    </main>\n  `;\n}\n',
    'src/style.css':'body {\n  margin: 0;\n  padding: 2rem;\n  font-family: system-ui, sans-serif;\n}\n\nmain {\n  max-width: 48rem;\n  margin: 0 auto;\n}\n',
  }),
  starter('vue-vite', 'Vue + Vite', 'playground', 'web', profile('vue-vite','JavaScript','Node.js','src/main.js',{BUILD:'vite',PREVIEW:'vite'},undefined,{port:'auto'},'vue-vite'), {
    'package.json':jsonSource({ type:'module', scripts:{ build:'vite build' }, dependencies:{ vite:'8.2.1', vue:'3.5.42', '@vitejs/plugin-vue':'6.0.8' } }),
    'vite.config.js':'import vue from "@vitejs/plugin-vue";\n\nexport default {\n  plugins: [vue()],\n};\n',
    'index.html':'<!doctype html>\n<html lang="en">\n  <head>\n    <meta charset="UTF-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n    <title>Learnitive</title>\n  </head>\n  <body>\n    <div id="app"></div>\n    <script type="module" src="/src/main.js"></script>\n  </body>\n</html>\n',
    'src/main.js':'import { createApp } from "vue";\nimport App from "./App.vue";\n\ncreateApp(App).mount("#app");\n',
    'src/App.vue':'<script setup>\nconst message = "Vue + Vite is running";\n</script>\n\n<template>\n  <main>\n    <h1>{{ message }}</h1>\n    <p>Learnitive Web Preview</p>\n  </main>\n</template>\n\n<style>\nbody {\n  margin: 0;\n  padding: 2rem;\n  font-family: system-ui, sans-serif;\n}\n\nmain {\n  max-width: 48rem;\n  margin: 0 auto;\n}\n</style>\n',
  }),
  starter('markdown', 'Markdown', 'playground', 'documents', profile('markdown','Markdown','Bangle visual editor','main.md',{BUILD:'markdown',PREVIEW:'markdown',EXPORT:'markdown'},undefined,undefined,'markdown'), {'main.md':'# Learnitive\n\nWrite **Markdown** visually with the built-in editor.\n\n- Format text\n- Add lists and links\n- Keep portable Markdown source\n'}),
  starter('interactive-doc', 'Blank document', 'playground', 'documents', interactiveDocProfile(), {'document.interactive.json':interactiveDocSource([])}),
  starter('interactive-doc-technical-documentation', 'Technical documentation', 'playground', 'documents', interactiveDocProfile(), {'document.interactive.json':interactiveDocSource([
    {id:'header-overview',type:'header',data:{text:'Overview',level:2}},
    {id:'paragraph-overview',type:'paragraph',data:{text:'Explain what this system or feature does, who it is for, and the outcome readers can expect.'}},
    {id:'header-installation',type:'header',data:{text:'Installation',level:2}},
    {id:'list-prerequisites',type:'list',data:{style:'unordered',items:[{content:'Prerequisite one',items:[]},{content:'Prerequisite two',items:[]}]}},
    {id:'header-configuration',type:'header',data:{text:'Configuration',level:2}},
    {id:'code-example',type:'code',data:{language:'shell',code:'# Add a configuration example here',runnable:false}},
    {id:'header-troubleshooting',type:'header',data:{text:'Troubleshooting',level:2}},
    {id:'paragraph-troubleshooting',type:'paragraph',data:{text:'Document common symptoms, causes, and fixes.'}},
  ])}),
  starter('interactive-doc-tutorial', 'Tutorial', 'playground', 'documents', interactiveDocProfile(), {'document.interactive.json':interactiveDocSource([
    {id:'header-goal',type:'header',data:{text:'What you will build',level:2}},
    {id:'paragraph-goal',type:'paragraph',data:{text:'Describe the result of this tutorial and what the reader will learn.'}},
    {id:'header-before',type:'header',data:{text:'Before you begin',level:2}},
    {id:'list-before',type:'list',data:{style:'unordered',items:[{content:'Required tool or account',items:[]},{content:'Background knowledge',items:[]}]}},
    {id:'header-step-one',type:'header',data:{text:'Step 1',level:2}},
    {id:'paragraph-step-one',type:'paragraph',data:{text:'Explain the first action and why it matters.'}},
    {id:'code-step-one',type:'code',data:{language:'shell',code:'# Add a command or code example',runnable:false}},
    {id:'header-next',type:'header',data:{text:'Next steps',level:2}},
    {id:'paragraph-next',type:'paragraph',data:{text:'Point readers to useful follow-up work or deeper topics.'}},
  ])}),
  starter('interactive-doc-research-notes', 'Research notes', 'playground', 'documents', interactiveDocProfile(), {'document.interactive.json':interactiveDocSource([
    {id:'header-purpose',type:'header',data:{text:'Research purpose',level:2}},
    {id:'paragraph-purpose',type:'paragraph',data:{text:'State the question, decision, or problem this research should inform.'}},
    {id:'header-questions',type:'header',data:{text:'Questions',level:2}},
    {id:'list-questions',type:'list',data:{style:'unordered',items:[{content:'Question one',items:[]},{content:'Question two',items:[]}]}},
    {id:'header-findings',type:'header',data:{text:'Findings',level:2}},
    {id:'paragraph-findings',type:'paragraph',data:{text:'Capture evidence, observations, and useful quotes in your own words.'}},
    {id:'header-sources',type:'header',data:{text:'Sources',level:2}},
    {id:'list-sources',type:'list',data:{style:'unordered',items:[{content:'Source — link or citation',items:[]}]}},
  ])}),
  starter('interactive-doc-project-proposal', 'Project proposal', 'playground', 'documents', interactiveDocProfile(), {'document.interactive.json':interactiveDocSource([
    {id:'header-summary',type:'header',data:{text:'Executive summary',level:2}},
    {id:'paragraph-summary',type:'paragraph',data:{text:'Describe the proposed project, the problem it addresses, and the expected value.'}},
    {id:'header-goals',type:'header',data:{text:'Goals',level:2}},
    {id:'list-goals',type:'list',data:{style:'unordered',items:[{content:'Primary goal',items:[]},{content:'Success metric',items:[]}]}},
    {id:'header-scope',type:'header',data:{text:'Scope',level:2}},
    {id:'paragraph-scope',type:'paragraph',data:{text:'Define what is included and what is explicitly out of scope.'}},
    {id:'header-risks',type:'header',data:{text:'Risks and mitigations',level:2}},
    {id:'paragraph-risks',type:'paragraph',data:{text:'List major risks and the actions that reduce them.'}},
    {id:'header-timeline',type:'header',data:{text:'Timeline',level:2}},
    {id:'paragraph-timeline',type:'paragraph',data:{text:'Add milestones, owners, and target dates.'}},
  ])}),
  starter('interactive-doc-meeting-notes', 'Meeting notes', 'playground', 'documents', interactiveDocProfile(), {'document.interactive.json':interactiveDocSource([
    {id:'header-agenda',type:'header',data:{text:'Agenda',level:2}},
    {id:'list-agenda',type:'list',data:{style:'unordered',items:[{content:'Topic one',items:[]},{content:'Topic two',items:[]}]}},
    {id:'header-notes',type:'header',data:{text:'Notes',level:2}},
    {id:'paragraph-notes',type:'paragraph',data:{text:'Capture the important discussion, context, and open questions.'}},
    {id:'header-decisions',type:'header',data:{text:'Decisions',level:2}},
    {id:'list-decisions',type:'list',data:{style:'unordered',items:[{content:'Decision and rationale',items:[]}]}},
    {id:'header-actions',type:'header',data:{text:'Action items',level:2}},
    {id:'checklist-actions',type:'checklist',data:{items:[{text:'Action — owner — due date',checked:false}]}},
  ])}),
  starter('interactive-doc-article', 'Article', 'playground', 'documents', interactiveDocProfile(), {'document.interactive.json':interactiveDocSource([
    {id:'header-introduction',type:'header',data:{text:'Introduction',level:2}},
    {id:'paragraph-introduction',type:'paragraph',data:{text:'Open with the problem, idea, or story that gives the reader a reason to continue.'}},
    {id:'header-main',type:'header',data:{text:'Main idea',level:2}},
    {id:'paragraph-main',type:'paragraph',data:{text:'Develop the argument with useful detail, evidence, examples, and clear transitions.'}},
    {id:'header-takeaways',type:'header',data:{text:'Key takeaways',level:2}},
    {id:'list-takeaways',type:'list',data:{style:'unordered',items:[{content:'Main takeaway',items:[]},{content:'Practical next step',items:[]}]}},
  ])}),
  starter('latex', 'Simple LaTeX Article', 'playground', 'documents', profile('latex','LaTeX','TeX Live','main.tex',{BUILD:'latex',PREVIEW:'latex',EXPORT:'latex'},undefined,undefined,'latex'), {'main.tex':'\\documentclass{article}\n\\begin{document}\nHello from Learnitive.\n\\end{document}\n'}),

  starter('node', 'Node.js', 'full-project', 'programming', profile('node','JavaScript','Node.js','src/index.js',{RUN:'node',TEST:'node'},undefined,undefined,'node-javascript'), {'package.json':jsonSource({ scripts:{ start:'node src/index.js', test:'node --test' }, type:'module' }),'src/index.js':js}),
  starter('node-typescript', 'Node.js + TypeScript', 'full-project', 'programming', profile('node','TypeScript','Node.js','src/index.ts',{RUN:'typescript'},undefined,undefined,'node-typescript'), {'package.json':jsonSource({ type:'module' }),'src/index.ts':'const message: string = "Hello from Learnitive";\nconsole.log(message);\n'}),
  starter('svelte-vite', 'Svelte + Vite', 'full-project', 'web', profile('svelte-vite','JavaScript','Node.js','src/main.js',{BUILD:'vite',PREVIEW:'vite'},undefined,{port:'auto'},'svelte-vite'), {
    'package.json':jsonSource({ type:'module', scripts:{ build:'vite build' }, dependencies:{ vite:'8.2.1', svelte:'5.57.0', '@sveltejs/vite-plugin-svelte':'7.3.0' } }),
    'vite.config.js':'import { svelte } from "@sveltejs/vite-plugin-svelte";\n\nexport default {\n  plugins: [svelte()],\n};\n',
    'index.html':'<!doctype html>\n<html lang="en">\n  <head>\n    <meta charset="UTF-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n    <title>Learnitive</title>\n  </head>\n  <body>\n    <div id="app"></div>\n    <script type="module" src="/src/main.js"></script>\n  </body>\n</html>\n',
    'src/main.js':'import { mount } from "svelte";\nimport App from "./App.svelte";\n\nconst target = document.getElementById("app");\n\nif (target) {\n  mount(App, { target });\n}\n',
    'src/App.svelte':'<script>\n  const message = "Svelte + Vite is running";\n</script>\n\n<main>\n  <h1>{message}</h1>\n  <p>Learnitive Web Preview</p>\n</main>\n\n<style>\n  :global(body) {\n    margin: 0;\n    padding: 2rem;\n    font-family: system-ui, sans-serif;\n  }\n\n  main {\n    max-width: 48rem;\n    margin: 0 auto;\n  }\n</style>\n',
  }),
  starter('next', 'Next.js', 'full-project', 'web', profile('next','TypeScript','Node.js','app/page.tsx',{RUN:'next',BUILD:'next',PREVIEW:'next'},{dev:'npm run dev',build:'npm run build'},{port:'auto'}), {'package.json':jsonSource({ scripts:{ dev:'next dev -H 0.0.0.0', build:'next build' }, dependencies:{ next:'latest', react:'latest', 'react-dom':'latest' } }),'app/page.tsx':'export default function Page() {\n  return <h1>Learnitive</h1>;\n}\n'}),
  starter('express', 'Express', 'full-project', 'web', profile('express','JavaScript','Node.js','server.js',{RUN:'express',PREVIEW:'express'},{run:'node server.js'},{port:'auto'}), {'package.json':jsonSource({ type:'module', dependencies:{ express:'latest' } }),'server.js':'import express from "express";\n\nconst app = express();\n\napp.get("/", (_request, response) => {\n  response.send("Learnitive");\n});\n\napp.listen(process.env.PORT || 3000, "0.0.0.0");\n'}),
  starter('flask', 'Flask', 'full-project', 'web', profile('flask','Python','Python','app.py',{RUN:'flask',PREVIEW:'flask'},undefined,{port:'auto'}), {'app.py':'from flask import Flask\n\napp = Flask(__name__)\n\n\n@app.get("/")\ndef home():\n    return "Learnitive"\n\n\nif __name__ == "__main__":\n    app.run(host="0.0.0.0", port=3000)\n','requirements.txt':'Flask\n'}),
  starter('fastapi', 'FastAPI', 'full-project', 'web', profile('fastapi','Python','Python','main.py',{RUN:'fastapi',PREVIEW:'fastapi'},undefined,{port:'auto'}), {'main.py':'from fastapi import FastAPI\n\napp = FastAPI()\n\n\n@app.get("/")\ndef home():\n    return {"hello": "Learnitive"}\n','requirements.txt':'fastapi\nuvicorn\n'}),
  starter('django', 'Django', 'full-project', 'web', profile('django','Python','Python','manage.py',{RUN:'django',PREVIEW:'django'},undefined,{port:'auto'}), {
    'requirements.txt':'Django\n','manage.py':'#!/usr/bin/env python\nimport os\nimport sys\n\nos.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")\n\nfrom django.core.management import execute_from_command_line\n\nexecute_from_command_line(sys.argv)\n',
    'project/__init__.py':'','project/settings.py':'SECRET_KEY = "development-only"\nDEBUG = True\nROOT_URLCONF = "project.urls"\nALLOWED_HOSTS = ["*"]\nINSTALLED_APPS = []\nMIDDLEWARE = []\n','project/urls.py':'from django.http import HttpResponse\nfrom django.urls import path\n\n\ndef home(_request):\n    return HttpResponse("Learnitive")\n\n\nurlpatterns = [path("", home)]\n',
  }),
  starter('pandoc', 'Pandoc Document', 'full-project', 'documents', profile('pandoc','Markdown','Pandoc','document.md',{BUILD:'markdown',PREVIEW:'markdown',EXPORT:'markdown'}), {'document.md':'# Document\n\nCreated in Learnitive.\n'}),
  starter('make', 'Make', 'full-project', 'build-systems', profile('make',undefined,'Make',undefined,{BUILD:'make',TEST:'make'}), {'Makefile':'all:\n\t@echo "Build with Learnitive"\n\ntest:\n\t@echo "Tests passed"\n'}),
  starter('cmake', 'CMake', 'full-project', 'build-systems', profile('cmake','C++','CMake','main.cpp',{BUILD:'cmake',TEST:'cmake'}), {'CMakeLists.txt':'cmake_minimum_required(VERSION 3.20)\nproject(app)\n\nadd_executable(app main.cpp)\n','main.cpp':'int main() {\n  return 0;\n}\n'}),
  starter('cargo', 'Cargo', 'full-project', 'build-systems', profile('cargo','Rust','Cargo','src/main.rs',{RUN:'rust',BUILD:'rust',TEST:'rust'}), {'Cargo.toml':'[package]\nname = "learnitive-app"\nversion = "0.1.0"\nedition = "2021"\n','src/main.rs':'fn main() {\n    println!("Learnitive");\n}\n'}),
  starter('maven', 'Maven', 'full-project', 'build-systems', profile('maven','Java','Maven',undefined,{BUILD:'maven',TEST:'maven'}), {'pom.xml':'<project xmlns="http://maven.apache.org/POM/4.0.0">\n  <modelVersion>4.0.0</modelVersion>\n  <groupId>dev.learnitive</groupId>\n  <artifactId>app</artifactId>\n  <version>1.0.0</version>\n</project>\n'}),
  starter('gradle', 'Gradle', 'full-project', 'build-systems', profile('gradle','Java','Gradle',undefined,{BUILD:'gradle',TEST:'gradle'}), {'settings.gradle':'rootProject.name = "learnitive-app"\n','build.gradle':'plugins {\n  id "java"\n}\n'}),
  starter('npm', 'npm', 'full-project', 'build-systems', profile('npm','JavaScript','Node.js',undefined,{RUN:'node',BUILD:'node',TEST:'node'}), {'package.json':jsonSource({ scripts:{ build:'echo build', test:'node --test' } })}),
  starter('pnpm', 'pnpm', 'full-project', 'build-systems', profile('pnpm','JavaScript','Node.js',undefined,{RUN:'node',BUILD:'node',TEST:'node'}), {'package.json':jsonSource({ packageManager:'pnpm@11.6.0', scripts:{ build:'echo build', test:'node --test' } }),'pnpm-workspace.yaml':'packages:\n  - packages/*\n'}),
  starter('blank', 'Blank', 'full-project', 'other', profile('blank',undefined,undefined,undefined,{}), {}),
]);


const LANGUAGE_DEFINITIONS: readonly ProjectLanguageDefinition[] = [
  { id:'python', label:'Python', iconText:'Py', extensions:['.py','.pyi'], popular:true, defaultStarterId:'python', starterIds:['python','flask','fastapi','django'], category:'programming', resultMode:'console', executable:true },
  { id:'javascript', label:'JavaScript', iconText:'JS', extensions:['.js','.mjs','.cjs'], popular:true, defaultStarterId:'javascript', starterIds:['javascript','node','react-vite','vue-vite','svelte-vite','next','express'], category:'programming', resultMode:'console', executable:true },
  { id:'typescript', label:'TypeScript', iconText:'TS', extensions:['.ts','.mts','.cts'], popular:true, defaultStarterId:'typescript', starterIds:['typescript','react-vite','vue-vite','svelte-vite','next'], category:'programming', resultMode:'console', executable:true },
  { id:'jsx', label:'JSX', iconText:'JSX', extensions:['.jsx'], popular:false, starterIds:[], category:'web', resultMode:'console', executable:true },
  { id:'tsx', label:'TSX', iconText:'TSX', extensions:['.tsx'], popular:false, starterIds:[], category:'web', resultMode:'console', executable:true },
  { id:'html', label:'HTML', iconText:'5', extensions:['.html','.htm'], popular:true, defaultStarterId:'html-css-js', starterIds:['html-css-js'], category:'web', resultMode:'preview', executable:false },
  { id:'c', label:'C', iconText:'C', extensions:['.c','.h'], popular:true, defaultStarterId:'c', starterIds:['c','make'], category:'programming', resultMode:'console', executable:true },
  { id:'cpp', label:'C++', iconText:'C++', extensions:['.cc','.cpp','.cxx','.hh','.hpp'], popular:true, defaultStarterId:'cpp', starterIds:['cpp','cmake'], category:'programming', resultMode:'console', executable:true },
  { id:'java', label:'Java', iconText:'J', extensions:['.java'], popular:true, defaultStarterId:'java', starterIds:['java','maven','gradle'], category:'programming', resultMode:'console', executable:true },
  { id:'php', label:'PHP', iconText:'php', extensions:['.php'], popular:true, defaultStarterId:'php', starterIds:['php'], category:'programming', resultMode:'console', executable:true },
  { id:'go', label:'Go', iconText:'Go', extensions:['.go'], popular:true, defaultStarterId:'go', starterIds:['go'], category:'programming', resultMode:'console', executable:true },
  { id:'rust', label:'Rust', iconText:'R', extensions:['.rs'], popular:true, defaultStarterId:'rust', starterIds:['rust','cargo'], category:'programming', resultMode:'console', executable:true },
  { id:'r', label:'R', iconText:'R', extensions:['.r'], popular:true, defaultStarterId:'r', starterIds:['r'], category:'programming', resultMode:'console', executable:true },
  { id:'latex', label:'LaTeX', iconText:'TeX', extensions:['.tex','.ltx'], popular:true, defaultStarterId:'latex', starterIds:['latex'], category:'documents', resultMode:'pdf', executable:true },
  { id:'ruby', label:'Ruby', iconText:'Rb', extensions:['.rb'], popular:false, defaultStarterId:'ruby', starterIds:['ruby'], category:'programming', resultMode:'console', executable:true },
  { id:'shell', label:'Bash', iconText:'$_', extensions:['.sh','.bash','.zsh','.fish'], popular:false, defaultStarterId:'shell', starterIds:['shell'], category:'programming', resultMode:'console', executable:true },
  { id:'markdown', label:'Markdown', iconText:'MD', extensions:['.md','.markdown'], popular:false, defaultStarterId:'markdown', starterIds:['markdown','pandoc'], category:'documents', resultMode:'preview', executable:false },
  { id:'plain', label:'Interactive Content', iconText:'DOC', extensions:['.interactive.json'], popular:false, defaultStarterId:'interactive-doc', starterIds:['interactive-doc','interactive-doc-technical-documentation','interactive-doc-tutorial','interactive-doc-research-notes','interactive-doc-project-proposal','interactive-doc-meeting-notes','interactive-doc-article'], category:'documents', resultMode:'preview', executable:false },
  { id:'css', label:'CSS', iconText:'CSS', extensions:['.css','.less'], popular:false, starterIds:[], category:'web', resultMode:'preview', executable:false },
  { id:'scss', label:'SCSS', iconText:'SCSS', extensions:['.scss','.sass'], popular:false, starterIds:[], category:'web', resultMode:'preview', executable:false },
  { id:'json', label:'JSON', iconText:'{}', extensions:['.json','.jsonl','.ndjson'], popular:false, starterIds:[], category:'data', resultMode:'none', executable:false },
  { id:'yaml', label:'YAML', iconText:'YML', extensions:['.yaml','.yml'], popular:false, starterIds:[], category:'data', resultMode:'none', executable:false },
  { id:'sql', label:'SQL', iconText:'SQL', extensions:['.sql'], popular:false, starterIds:[], category:'data', resultMode:'none', executable:false },
  { id:'toml', label:'TOML', iconText:'TOML', extensions:['.toml'], popular:false, starterIds:[], category:'data', resultMode:'none', executable:false },
];

export const PROJECT_LANGUAGE_CATALOG: readonly ProjectLanguageDefinition[] = Object.freeze(LANGUAGE_DEFINITIONS.map((item) => Object.freeze({ ...item, extensions:Object.freeze([...item.extensions]), starterIds:Object.freeze([...item.starterIds]) })));


export const PROJECT_MODE_CATALOG: readonly ProjectModeDefinition[] = Object.freeze(
  (projectModeCatalogData as readonly ProjectModeDefinition[]).map((item) => Object.freeze({ ...item })),
);

const MODE_BY_ID = new Map(PROJECT_MODE_CATALOG.map((item) => [item.id, item]));
const MODE_BY_STARTER = new Map(PROJECT_MODE_CATALOG.map((item) => [item.starterId, item]));
export const POPULAR_PROJECT_MODES: readonly ProjectModeDefinition[] = Object.freeze(PROJECT_MODE_CATALOG.filter((item) => item.popular));
export const POPULAR_LANGUAGE_PROJECT_MODES: readonly ProjectModeDefinition[] = Object.freeze(
  ['python','javascript','html','c','cpp','java','php','go','rust','ruby','r','shell','markdown','latex']
    .map((id) => MODE_BY_ID.get(id as ProjectModeId))
    .filter((item): item is ProjectModeDefinition => Boolean(item)),
);
export const POPULAR_FRAMEWORK_PROJECT_MODES: readonly ProjectModeDefinition[] = Object.freeze(
  ['node-javascript','typescript-vite','react-vite-typescript','vue-vite','svelte-vite']
    .map((id) => MODE_BY_ID.get(id as ProjectModeId))
    .filter((item): item is ProjectModeDefinition => Boolean(item)),
);
export function projectModeById(id: string | undefined): ProjectModeDefinition | undefined { return id ? MODE_BY_ID.get(id as ProjectModeId) : undefined; }
export function isRunnableSnippetMode(mode: ProjectModeDefinition | undefined): mode is ProjectModeDefinition {
  return Boolean(mode && mode.projectType === 'code' && mode.runtime?.operation === 'RUN' && mode.runtime.provider);
}
export function runnableSnippetModes(): readonly ProjectModeDefinition[] {
  const byLanguage = new Map<ProjectLanguageId, ProjectModeDefinition>();
  for (const mode of PROJECT_MODE_CATALOG) {
    if (!isRunnableSnippetMode(mode)) continue;
    const current = byLanguage.get(mode.primaryLanguageId);
    if (!current || mode.id === mode.primaryLanguageId) byLanguage.set(mode.primaryLanguageId, mode);
  }
  return Object.freeze([...byLanguage.values()]);
}
export function snippetStarterForMode(id: string | undefined): { mode: ProjectModeDefinition; filename: string; code: string } | undefined {
  const mode = projectModeById(id);
  if (!isRunnableSnippetMode(mode)) return undefined;
  const starterValue = starterById(mode.starterId);
  const filename = mode.defaultEntryFile || starterValue?.profile.entry || Object.keys(starterValue?.files || {})[0] || 'main.txt';
  const code = String(starterValue?.files?.[filename] ?? starterValue?.files?.[Object.keys(starterValue?.files || {})[0] || ''] ?? '');
  return { mode, filename, code };
}
export function projectModeForStarter(starterId: string): ProjectModeDefinition | undefined { return MODE_BY_STARTER.get(starterId); }
export function projectModeForProfile(value: ProjectProfile | undefined): ProjectModeDefinition | undefined {
  if (!value) return undefined;
  if (value.modeId) { const direct = projectModeById(value.modeId); if (direct) return direct; }
  const entry = value.entry || '';
  if (value.projectType === 'latex') return projectModeById('latex');
  if (value.projectType === 'static-web') return projectModeById('html');
  if (value.projectType === 'react-vite') return /\.(?:jsx|js)$/iu.test(entry) ? projectModeById('react-vite-javascript') : projectModeById('react-vite-typescript');
  if (value.projectType === 'typescript-vite') return projectModeById('typescript-vite');
  if (value.projectType === 'vue-vite') return projectModeById('vue-vite');
  if (value.projectType === 'svelte-vite') return projectModeById('svelte-vite');
  if (value.projectType === 'node') return /\.(?:ts|tsx)$/iu.test(entry) ? projectModeById('node-typescript') : projectModeById('node-javascript');
  if (['python','c','cpp','java','go','rust','php','ruby','r','shell','markdown','javascript','typescript'].includes(value.projectType)) return projectModeById(value.projectType);
  return undefined;
}
export function projectCapabilitiesForProfile(value: ProjectProfile | undefined): ProjectCapabilities {
  const mode = projectModeForProfile(value);
  const latex = Boolean(mode?.id === 'latex' || value?.projectType === 'latex' || value?.providers.BUILD === 'latex' || value?.providers.PREVIEW === 'latex');
  return Object.freeze({
    projectSearch: true,
    diagnostics: true,
    fileRefactor: true,
    latexTools: latex,
    paperMemory: latex,
    latexAiActions: latex,
  });
}
export function canonicalProfileForMode(id: ProjectModeId): ProjectProfile {
  const mode = projectModeById(id);
  if (!mode) throw new Error(`Unknown project mode: ${id}`);
  const starterValue = starterById(mode.starterId);
  if (!starterValue) throw new Error(`Missing starter for project mode: ${id}`);
  return { ...structuredClone(starterValue.profile), modeId: id, entry: mode.defaultEntryFile };
}
export function projectModeIdForStarter(starterId: string): ProjectModeId | undefined { return projectModeForStarter(starterId)?.id; }
export function projectTypeForProfile(value: ProjectProfile | undefined): ProjectType | undefined { return projectModeForProfile(value)?.projectType; }
export function projectOutputForProfile(value: ProjectProfile | undefined): ProjectOutputKind | undefined { return projectModeForProfile(value)?.defaultOutput; }
const LANGUAGE_BY_ID = new Map(PROJECT_LANGUAGE_CATALOG.map((item) => [item.id, item]));
const POPULAR_LANGUAGE_IDS = new Set(POPULAR_LANGUAGE_PROJECT_MODES.map((mode) => mode.primaryLanguageId));
export const POPULAR_PROJECT_LANGUAGES: readonly ProjectLanguageDefinition[] = Object.freeze(
  [...POPULAR_LANGUAGE_IDS].map((id) => LANGUAGE_BY_ID.get(id)).filter((item): item is ProjectLanguageDefinition => Boolean(item)),
);
export const ADDITIONAL_PROJECT_LANGUAGES: readonly ProjectLanguageDefinition[] = Object.freeze(
  PROJECT_LANGUAGE_CATALOG.filter((item) => Boolean(item.defaultStarterId) && !POPULAR_LANGUAGE_IDS.has(item.id)),
);
const LANGUAGE_BY_EXTENSION = new Map(PROJECT_LANGUAGE_CATALOG.flatMap((item) => item.extensions.map((extension) => [extension, item] as const)));
const LANGUAGE_BY_STARTER = new Map(PROJECT_LANGUAGE_CATALOG.flatMap((item) => item.starterIds.map((starterId) => [starterId, item] as const)));

export function projectLanguageById(id: ProjectLanguageId): ProjectLanguageDefinition | undefined { return LANGUAGE_BY_ID.get(id); }
export function projectLanguageForStarter(starterId: string): ProjectLanguageDefinition | undefined { return LANGUAGE_BY_STARTER.get(starterId); }
export function resolveProjectLanguage(path: string): ProjectLanguageDefinition | undefined {
  const normalized = String(path || '').toLowerCase().replaceAll('\\','/');
  const leaf = normalized.slice(normalized.lastIndexOf('/') + 1);
  if (leaf === 'dockerfile' || leaf === 'makefile') return LANGUAGE_BY_ID.get('shell');
  const dot = leaf.lastIndexOf('.');
  return dot >= 0 ? LANGUAGE_BY_EXTENSION.get(leaf.slice(dot)) : undefined;
}

export function resolveProjectRelativePath(baseFile: string, reference: string): string | undefined {
  const raw = String(reference || '').trim().split(/[?#]/u, 1)[0]?.replaceAll('\\', '/');
  if (!raw || raw.includes('\0') || /^(?:[a-z][a-z0-9+.-]*:|\/\/|\/|#)/iu.test(raw)) return undefined;
  const base = String(baseFile || '').replaceAll('\\', '/');
  const slash = base.lastIndexOf('/');
  const stack = (slash < 0 ? '' : base.slice(0, slash)).split('/').filter(Boolean);
  for (const part of raw.split('/')) {
    if (!part || part === '.') continue;
    if (part === '..') {
      if (!stack.length) return undefined;
      stack.pop();
      continue;
    }
    if (part.includes('\0')) return undefined;
    stack.push(part);
  }
  return stack.length ? stack.join('/') : undefined;
}

const FRIENDLY_PROJECT_WORDS = Object.freeze([
  'orbit','meadow','cedar','lantern','summit','harbor','comet','maple','canyon','ripple',
  'willow','ember','forest','river','cloud','falcon','grove','island','juniper','pebble',
]);
const recentProjectNameWords: string[] = [];
function randomIndex(maxExclusive: number): number {
  if (maxExclusive <= 1) return 0;
  try {
    const values = new Uint32Array(1);
    globalThis.crypto?.getRandomValues?.(values);
    return values[0] % maxExclusive;
  } catch {
    return Math.floor(Math.random() * maxExclusive);
  }
}
function nextFriendlyProjectWord(): string {
  const recent = new Set(recentProjectNameWords.slice(-(FRIENDLY_PROJECT_WORDS.length - 1)));
  const available = FRIENDLY_PROJECT_WORDS.filter((word) => !recent.has(word));
  const word = available[randomIndex(available.length)] || FRIENDLY_PROJECT_WORDS[0];
  recentProjectNameWords.push(word);
  if (recentProjectNameWords.length > FRIENDLY_PROJECT_WORDS.length - 1) recentProjectNameWords.shift();
  return word;
}
export function defaultProjectName(starterId: string): string {
  const mode = projectModeForStarter(starterId);
  const fallback = projectLanguageForStarter(starterId)?.id || 'project';
  const prefix = (mode?.namingPrefix || fallback).replace(/[^a-z0-9-]+/giu,'-').replace(/^-+|-+$/gu,'').toLowerCase() || 'project';
  return `${prefix}-${nextFriendlyProjectWord()}`;
}

const STARTER_BY_ID = new Map(PROJECT_STARTERS.map((item) => [item.id, item]));
export function starterById(id: string): ProjectStarter | undefined { return STARTER_BY_ID.get(id); }
export function startersForSurface(surface: ProjectSurface): readonly ProjectStarter[] { return PROJECT_STARTERS.filter((item) => item.surface === surface); }
export function materializeStarter(id: string): { profile: ProjectProfile; files: Record<string, string> } {
  const item = starterById(id); if (!item) throw new Error(`Unknown project starter: ${id}`);
  return { profile: structuredClone(item.profile), files: { ...item.files } };
}

const KNOWN_CONFIG_SECTIONS = new Set(['project','commands','providers','preview']);
const OPERATION_KEYS: Record<string, ProjectOperation> = { run:'RUN', build:'BUILD', test:'TEST', preview:'PREVIEW', export:'EXPORT' };
function scalar(value: string): string | number {
  const v = value.trim().replace(/^['"]|['"]$/gu, '');
  if (/^[0-9]+$/u.test(v)) return Number(v);
  return v;
}

export function parseLearnitiveConfig(text: string): Partial<ProjectProfile> {
  if (typeof text !== 'string' || Buffer.byteLength(text, 'utf8') > 64 * 1024) throw new Error('Invalid .learnitive.yml size');
  if (/\t|[&*!]|<<:/u.test(text)) throw new Error('Unsupported .learnitive.yml syntax');
  const result: Partial<ProjectProfile> = { providers: {}, commands: {} };
  let section = '';
  for (const raw of text.split(/\r?\n/u)) {
    const withoutComment = raw.replace(/\s+#.*$/u,''); if (!withoutComment.trim()) continue;
    const indent = withoutComment.match(/^ */u)?.[0].length ?? 0;
    if (indent > 2 || indent % 2) throw new Error('Unsupported .learnitive.yml nesting');
    const match = withoutComment.trim().match(/^([A-Za-z][A-Za-z0-9_-]*):(?:\s*(.*))?$/u); if (!match) throw new Error('Invalid .learnitive.yml line');
    const [, key, rawValue=''] = match;
    if (indent === 0) {
      if (key === 'version') { if (Number(rawValue) !== 1) throw new Error('Unsupported .learnitive.yml version'); continue; }
      if (!KNOWN_CONFIG_SECTIONS.has(key)) throw new Error(`Unknown .learnitive.yml key: ${key}`);
      if (rawValue.trim()) throw new Error(`Section ${key} must be a mapping`); section = key; continue;
    }
    const value = scalar(rawValue);
    if (section === 'project') {
      if (key === 'type') result.projectType = String(value);
      else if (key === 'language') result.language = String(value);
      else if (key === 'runtime') result.runtime = String(value);
      else if (key === 'entry') result.entry = safeRelativeConfigPath(String(value));
      else throw new Error(`Unknown project key: ${key}`);
    } else if (section === 'commands') {
      if (!['run','dev','build','test','export'].includes(key)) throw new Error(`Unknown commands key: ${key}`);
      result.commands![key as keyof NonNullable<ProjectProfile['commands']>] = String(value);
    } else if (section === 'providers') {
      const op = OPERATION_KEYS[key]; if (!op) throw new Error(`Unknown providers key: ${key}`);
      result.providers![op] = String(value) as ProjectProvider;
    } else if (section === 'preview') {
      if (key !== 'port') throw new Error(`Unknown preview key: ${key}`);
      if (value !== 'auto' && (!Number.isInteger(value) || Number(value) < 1 || Number(value) > 65535)) throw new Error('Invalid preview port');
      result.preview = { port: value as 'auto' | number };
    }
  }
  if (!result.projectType && !Object.keys(result.providers || {}).length && !Object.keys(result.commands || {}).length) throw new Error('Empty .learnitive.yml');
  return result;
}

function safeRelativeConfigPath(value: string): string {
  const v = value.replaceAll('\\','/').replace(/^\.\//u,'');
  if (!v || v.startsWith('/') || v.split('/').includes('..') || v.includes('\0')) throw new Error('Invalid project entry path');
  return v;
}

function mergeProfile(base: ProjectProfile, override: Partial<ProjectProfile>): ProjectProfile {
  return { ...base, ...override, version: 1, providers: { ...base.providers, ...(override.providers || {}) }, commands: { ...(base.commands || {}), ...(override.commands || {}) }, preview: override.preview || base.preview };
}

function target(root: string, label: string, p: ProjectProfile, confidence: number, reason: string): ProjectTarget {
  return { id: `${root || '.'}:${p.projectType}`, root, label, profile: p, confidence, reason };
}

function dirname(file: string): string { const i=file.lastIndexOf('/'); return i < 0 ? '' : file.slice(0,i); }
function basename(file: string): string { const i=file.lastIndexOf('/'); return i < 0 ? file : file.slice(i+1); }

export function detectProjectTargets(input: {
  files: Readonly<Record<string, string>>;
  selectedProfile?: ProjectProfile;
  portableConfigText?: string;
  activePath?: string;
}): ProjectTarget[] {
  const files = Object.fromEntries(Object.entries(input.files).map(([k,v]) => [k.replace(/^\.\//u,''), v]));
  const targets: ProjectTarget[] = [];
  const roots = new Set(['']);
  for (const name of Object.keys(files)) {
    if (['package.json','pyproject.toml','requirements.txt','Cargo.toml','go.mod','pom.xml','build.gradle','build.gradle.kts','CMakeLists.txt','Makefile'].includes(basename(name))) roots.add(dirname(name));
  }
  const portable = input.portableConfigText ? parseLearnitiveConfig(input.portableConfigText) : undefined;
  if (input.selectedProfile) targets.push(target('', input.selectedProfile.projectType, portable ? mergeProfile(input.selectedProfile, portable) : input.selectedProfile, portable ? 100 : 95, portable ? '.learnitive.yml + selected profile' : 'selected project profile'));
  else if (portable?.projectType) {
    const base = starterById(portable.projectType)?.profile || profile(portable.projectType,portable.language,portable.runtime,portable.entry,portable.providers || {},portable.commands,portable.preview);
    targets.push(target('', portable.projectType, mergeProfile(base, portable), 100, '.learnitive.yml'));
  }
  for (const root of roots) {
    const at = (name: string) => files[root ? `${root}/${name}` : name];
    if (at('package.json') !== undefined) {
      let pkg: any={}; try { pkg=JSON.parse(at('package.json')); } catch {}
      const deps={...(pkg.dependencies||{}),...(pkg.devDependencies||{})};
      if (at('vite.config.ts')!==undefined || at('vite.config.js')!==undefined || deps.vite) {
        const viteMode: ProjectModeId = deps.vue ? 'vue-vite' : deps.svelte ? 'svelte-vite' : deps.react
          ? (Object.keys(files).some((name)=>name.startsWith(root ? `${root}/` : '') && /\.(?:tsx|ts)$/iu.test(name)) ? 'react-vite-typescript' : 'react-vite-javascript')
          : 'typescript-vite';
        targets.push(target(root,'Vite',canonicalProfileForMode(viteMode),90,'Vite/package.json'));
      }
      else if (deps.next) targets.push(target(root,'Next.js',profile('next','TypeScript','Node.js',undefined,{RUN:'next',BUILD:'next',PREVIEW:'next'},{dev:'npm run dev',build:'npm run build'},{port:'auto'}),88,'Next.js package.json'));
      else if (deps.express) targets.push(target(root,'Express',profile('express','JavaScript','Node.js',undefined,{RUN:'express',PREVIEW:'express'},{run:pkg.scripts?.start || 'node server.js'},{port:'auto'}),86,'Express package.json'));
      else targets.push(target(root,'Node.js',profile('node','JavaScript','Node.js',at('src/index.js')!==undefined?'src/index.js':undefined,{RUN:'node',BUILD:'node',TEST:'node'},undefined,undefined,'node-javascript'),80,'package.json'));
    }
    if (at('pyproject.toml')!==undefined || at('requirements.txt')!==undefined) {
      const content=`${at('pyproject.toml')||''}\n${at('requirements.txt')||''}`;
      const type=/fastapi/iu.test(content)?'fastapi':/django/iu.test(content)?'django':/flask/iu.test(content)?'flask':'python';
      targets.push(target(root,type,profile(type,'Python','Python',at('main.py')!==undefined?'main.py':at('app.py')!==undefined?'app.py':undefined,{RUN:type==='python'?'python':type as ProjectProvider,PREVIEW:type==='python'?undefined:type as ProjectProvider,TEST:'python'}),82,'Python manifest'));
    }
    if (at('Cargo.toml')!==undefined) targets.push(target(root,'Cargo',profile('cargo','Rust','Cargo','src/main.rs',{RUN:'rust',BUILD:'rust',TEST:'rust'}),85,'Cargo.toml'));
    if (at('go.mod')!==undefined) targets.push(target(root,'Go',profile('go','Go','Go',undefined,{RUN:'go',BUILD:'go',TEST:'go'}),85,'go.mod'));
    if (at('pom.xml')!==undefined) targets.push(target(root,'Maven',profile('maven','Java','Maven',undefined,{BUILD:'maven',TEST:'maven'}),85,'pom.xml'));
    if (at('build.gradle')!==undefined || at('build.gradle.kts')!==undefined) targets.push(target(root,'Gradle',profile('gradle','Java','Gradle',undefined,{BUILD:'gradle',TEST:'gradle'}),85,'Gradle build'));
    if (at('CMakeLists.txt')!==undefined) targets.push(target(root,'CMake',profile('cmake','C++','CMake',undefined,{BUILD:'cmake',TEST:'cmake'}),85,'CMakeLists.txt'));
    if (at('Makefile')!==undefined) targets.push(target(root,'Make',profile('make',undefined,'Make',undefined,{BUILD:'make',TEST:'make'}),75,'Makefile'));
  }
  const texFiles=Object.keys(files).filter((name)=>/\.tex$/iu.test(name));
  const rootTex=texFiles.find((name)=>/\\documentclass/u.test(files[name]||'')) || texFiles.find((name)=>basename(name)==='main.tex');
  if (rootTex) targets.push(target(dirname(rootTex),'LaTeX',profile('latex','LaTeX','TeX Live',basename(rootTex),{BUILD:'latex',PREVIEW:'latex',EXPORT:'latex'}),78,'LaTeX root detection'));
  if (files['index.html']!==undefined) targets.push(target('','Static Web',profile('static-web','HTML/CSS/JS','Browser','index.html',{PREVIEW:'static-web'},undefined,{port:'auto'},'html'),70,'index.html'));
  if (!targets.length) {
    const candidates = Object.keys(files).sort((a,b)=>a.localeCompare(b,'en'));
    const rules: Array<[RegExp,string,ProjectProvider,string,ProjectModeId]> = [
      [/\.py$/iu,'python','python','Python','python'],[/\.(?:cpp|cc|cxx)$/iu,'cpp','cpp','C++','cpp'],[/\.c$/iu,'c','c','C','c'],
      [/\.java$/iu,'java','java','Java','java'],[/\.go$/iu,'go','go','Go','go'],[/\.rs$/iu,'rust','rust','Rust','rust'],
      [/\.php$/iu,'php','php','PHP','php'],[/\.rb$/iu,'ruby','ruby','Ruby','ruby'],[/\.r$/iu,'r','r','R','r'],[/\.(?:sh|bash)$/iu,'shell','shell','Bash','shell'],
      [/\.ts$/iu,'typescript','typescript','TypeScript','typescript'],[/\.js$/iu,'javascript','node','JavaScript','javascript'],
    ];
    for (const [pattern,type,provider,language,modeId] of rules) {
      const found = candidates.find((name)=>pattern.test(name));
      if (found) { targets.push(target(dirname(found),language,profile(type,language,language,basename(found),{RUN:provider},undefined,undefined,modeId),55,'source file detection')); break; }
    }
  }
  if (files['main.md']!==undefined || files['README.md']!==undefined || files['document.md']!==undefined) { const entry=files['main.md']!==undefined?'main.md':files['document.md']!==undefined?'document.md':'README.md'; targets.push(target('','Markdown',profile('markdown','Markdown','Bangle visual editor',entry,{BUILD:'markdown',PREVIEW:'markdown',EXPORT:'markdown'},undefined,undefined,'markdown'),60,'Markdown document')); }
  if (!targets.length && input.activePath) {
    const ext=input.activePath.split('.').pop()?.toLowerCase();
    const map: Record<string,[ProjectModeId,ProjectProvider,string]>={py:['python','python','Python'],js:['javascript','node','JavaScript'],ts:['typescript','typescript','TypeScript'],c:['c','c','C'],cpp:['cpp','cpp','C++'],cc:['cpp','cpp','C++'],java:['java','java','Java'],go:['go','go','Go'],rs:['rust','rust','Rust'],php:['php','php','PHP'],rb:['ruby','ruby','Ruby'],r:['r','r','R'],sh:['shell','shell','Bash'],bash:['shell','shell','Bash']};
    if (ext && map[ext]) { const [modeId,provider,language]=map[ext]; targets.push(target(dirname(input.activePath),language,profile(modeId,language,language,basename(input.activePath),{RUN:provider},undefined,undefined,modeId),40,'active file extension')); }
    if (ext && (ext === 'md' || ext === 'markdown')) targets.push(target(dirname(input.activePath),'Markdown',profile('markdown','Markdown','Bangle visual editor',basename(input.activePath),{BUILD:'markdown',PREVIEW:'markdown',EXPORT:'markdown'},undefined,undefined,'markdown'),40,'active Markdown file'));
  }
  const unique = new Map<string,ProjectTarget>();
  for (const item of targets.sort((a,b)=>b.confidence-a.confidence)) if (!unique.has(item.id)) unique.set(item.id,item);
  return [...unique.values()];
}

export function resolveOperation(profile: ProjectProfile, operation: ProjectOperation): { provider: ProjectProvider; command?: string } | null {
  const provider = profile.providers[operation]; if (!provider) return null;
  if (provider !== 'custom') return { provider };
  const key = operation === 'RUN' ? (profile.commands?.run ? 'run' : 'dev') : operation.toLowerCase() as 'build'|'test'|'export';
  return { provider, command: profile.commands?.[key as keyof NonNullable<ProjectProfile['commands']>] };
}
