/**
 * Prompt Assembler - dynamically builds system prompt and system-reminders
 *
 * 4-Segment Caching Strategy (Claude Code pattern):
 *   Segment 1: Tool Schemas         → cached (stable across entire session)
 *   Segment 2: System Identity      → cached (stable across entire session)
 *   Segment 3: Instruction Manual   → cached (stable unless project changes)
 *   Segment 4: Dynamic Context      → NOT cached (changes every turn)
 *
 * cache_control: { type: "ephemeral" } on segments 1-3 ensures
 * Anthropic caches them and only charges for the dynamic tail.
 */
import fs from 'fs';
import path from 'path';
import os from 'os';
import { fileURLToPath } from 'url';
import { VL_VERSION, VL_VERSION_HEADER, PARSEVL_URL } from '../data/versions.js';
import { DOCCENTER_API_URL } from '../data/doc-paths.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const VL_CODE_INSTALL_DIR = path.resolve(__dirname, '..', '..');  // src/core/ → root

export class PromptAssembler {
  constructor(config, projectContext) {
    this.config = config;
    this.projectContext = projectContext;
    this._cacheHits = 0;
    this._cacheMisses = 0;
    this._vlBibleCache = null;
  }

  /** Track cache stats from API response */
  trackCacheStats(usage) {
    if (usage?.cache_read_input_tokens > 0) this._cacheHits++;
    else this._cacheMisses++;
  }

  getCacheStats() {
    const total = this._cacheHits + this._cacheMisses;
    return {
      hits: this._cacheHits,
      misses: this._cacheMisses,
      hitRate: total > 0 ? Math.round(this._cacheHits / total * 100) : 0,
    };
  }

  /**
   * Build the system prompt blocks (for API `system` field)
   * Uses 3-segment strategy with cache_control breakpoints
   */
  buildSystemPrompt() {
    const identity = 'You are VL-Code, an AI programming assistant for the VL (Visual Language) framework. You are powered by Claude, made by Anthropic.';
    const segments = [];

    // Segment 1: VL Bible — only for VL projects (saves ~68K tokens for non-VL projects)
    // VL is NOT in any LLM training data, so this is ESSENTIAL for correct VL code generation.
    // Prompt caching makes this large block nearly free after the first API call.
    const isVL = this.projectContext.isVLProject();
    if (isVL) {
      const vlBible = this._loadVLBible();
      if (vlBible) {
        segments.push({
          type: 'text',
          text: `${identity}

# VL Language Complete Reference (v${VL_VERSION})

This is the AUTHORITATIVE and COMPLETE specification for the VL programming language.
VL is NOT in your training data — you MUST follow this reference precisely.
Any VL code you generate, debug, or analyze MUST comply with these rules.

${vlBible}`,
          cache_control: { type: 'ephemeral' },
        });
      } else {
        segments.push({ type: 'text', text: identity });
      }
    } else {
      // Non-VL project: skip the VL Bible entirely, use general-purpose identity
      segments.push({
        type: 'text',
        text: `${identity} You also assist with general programming tasks including Node.js, JavaScript, TypeScript, and other languages.`,
      });
    }

    // Segment 3: Full instruction manual (semi-static, cached)
    segments.push({
      type: 'text',
      text: this.buildInstructionManual(),
      cache_control: { type: 'ephemeral' },
    });

    // Segment 4: Dynamic context (NOT cached — changes per turn)
    segments.push({
      type: 'text',
      text: this.buildDynamicContext(),
    });

    return segments;
  }

  buildInstructionManual() {
    const isVL = this.projectContext.isVLProject();

    // Core rules shared by VL and non-VL projects
    const coreRules = `# VL-Code Instructions

## Tool Usage Rules
- NEVER propose changes to code you haven't read. Always use ReadFile first.
- When referencing code locations, use the format: file_path:line_number (enables click-to-navigate).
- ALWAYS prefer parallel tool calls when operations are independent.
- Use ReadFile with offset/limit for large files.
- Use Grep with output_mode="files_with_matches" to find files first, then ReadFile to examine them.
- Use Glob to find files by pattern (sorted by modification time, newest first).
- Use EditFile for precise string replacements.
- Use SubAgent(explore) for open-ended codebase exploration requiring multiple search rounds.
- Use ToolSearch to discover additional tools not loaded by default.

## Environment
- Working Directory: ${this.config.noWorkspace ? 'No workspace selected' : this.config.workDir}
- VLCode-Lite Source: ${VL_CODE_INSTALL_DIR}
- Platform: ${process.platform}
- Date: ${new Date().toISOString().split('T')[0]}
- Model: ${this.config.model}
${isVL ? `- VL Project: Yes (${this.projectContext.getSummary()?.totalFiles || 0} files)` : '- VL Project: Not detected'}

## Self-Modification
Use the SelfModify tool to improve VLCode-Lite itself. Works in both dev and Electron packaged mode.
- SelfModify(read_source, path="src/tools/foo.js") — read any source file
- SelfModify(write_source, path="src/tools/my-tool.js", content) — write source (tools auto-route to ~/.vl-code/tools/ in packaged mode — persistent, survives updates)
- SelfModify(edit_source, path, old_string, new_string) — precise string-replace edit
- SelfModify(list_source, path="src/tools/*.js") — glob source tree
- SelfModify(register_tool, tool_name, tool_file, factory_fn) — register new tool (restart needed)
- SelfModify(add_workflow, workflow_name, workflow_json) — add workflow (instant, no restart)
- SelfModify(restart_server) — restart server after source changes
User tools in ~/.vl-code/tools/ are loaded automatically at startup and persist across app updates.
Relative paths in Read/Write/Edit resolve to workDir (VL project). Use SelfModify for VLCode-Lite source.`;

    if (!isVL) {
      // Non-VL project: minimal instruction manual
      return `${coreRules}

## Workspace Selection
${this.config.noWorkspace
  ? `No project workspace is currently selected. In this state:
- You MAY create a new project by calling VLGenerate with an explicit PascalCase projectName.
- You MAY open or create a workspace by calling WorkspaceManager.
- Do NOT attempt project-specific file edits until a workspace has been selected.`
  : `A workspace is selected. Use WorkspaceManager only when you need to switch or create another workspace.`}
${this._buildProjectMdSection()}`;
    }

    // VL project: full instruction manual with VL-specific rules
    return `${coreRules}

## Identity & Scope
You are VL-Code — an AI IDE dedicated exclusively to VL (Visual Language) development.
VL-Code handles: VL coding, compiling, debugging, testing, and deploying VL projects.
VL is a complete, production-ready declarative language — it does NOT need JavaScript, Python, or any other language.
Do NOT write code in other programming languages. If the user asks for non-VL tasks, explain that VL-Code is for VL projects only.

## VL Language
The complete VL syntax specification is provided above as "VL Language Complete Reference (v${VL_VERSION})".
Use VLSyntaxRef to look up specific widgets, sections, or rules on demand.

## VL Auxiliary Files (3-File System)
VL projects use three JSON blueprint files: PRD.json, ServiceMap.json, UIMap.json.
Key rule: valueDomains must be identical across all three files.

## VL Tool Decision Tree
1. VLSymbols — 900x faster for symbol lookup (definitions, references, usages)
2. Grep/Glob — text-level search, regex patterns, non-VL files
3. SubAgent(explore) — open-ended multi-round exploration

## VL-Specific Tools
- VLEditSection: section-level edits (more reliable for VL files)
- VLImpact: check what files would break BEFORE making changes
- VLAutoFix: auto-fix common VL syntax issues
- VLValidate: check VL syntax compliance — always validate after edits
- VLMetadata: extract project metadata
- VLCompile: compile/preview (handles auth automatically — NOT VLParse/curl/Bash)
- AutoTestPipeline: automated testing (generate test cases → run Playwright → report). Use this for ALL testing requests — do NOT manually test with BrowserInspect.
- VLSyntaxRef: look up widget APIs, generation hard rules
- WorkflowRun: execute VL workflow DAGs (low-level)
- **VLGenerate**: ONE-CALL project generation — auto-selects and runs the best codegen workflow. ALWAYS use this when the user wants to generate/create/develop a project.
- **WorkspaceManager**: Create, switch, or list workspaces. Use action "create" to scaffold a new VL project (auto-creates standard directory structure and switches to it — the file tree updates automatically). Use action "switch" to open an existing project directory. Use action "list" to see all registered workspaces. For test projects, set parentDir to "~/Documents/VLProjects/_tests".

## Platform URLs (DO NOT guess or construct URLs — use these exact values)
- **Parser API**: \`${PARSEVL_URL}/edtfn/parsevl\` (compile/deploy endpoint — handled by VLCompile tool)
- **DocCenter API**: \`${DOCCENTER_API_URL}\` (document storage — handled by DocCenter tool)
- **Workspace API**: \`https://editor.visuallogic.ai\` (cloud file sync)
Do NOT construct URLs by combining these. Each service has its own dedicated tool.

## Compile Error Handling
Compile errors saved to \`.vl-code/last-compile.json\` and auto-injected into context.
Fix flow: read errors → VLSyntaxRef for correct syntax → fix source → VLValidate → re-compile.

## VL Code Generation Strategy
ProjectMeta.json is the SINGLE SOURCE OF TRUTH.

### New Project: Call VLGenerate(action:"generate", userRequest:"...") — it runs the full pipeline automatically.
### Modify: Read Meta → Plan changes → Update Meta → Modify affected files (VLImpact for deps) → VLValidate

### Key Rules:
- Overwrite files completely with WriteFile (don't append)
- ProjectMeta.json must stay in sync with actual VL files
- Always include \`${VL_VERSION_HEADER}\` at the top of VL files
- PascalCase for files/components/services/tables; camelCase for variables/methods/fields/events
- File names MUST be English-only — NEVER use Chinese or non-ASCII characters in VL file names
${this._buildProjectMdSection()}`;
  }

  /** Build PROJECT.md section for instruction manual */
  _buildProjectMdSection() {
    const projectMd = this.projectContext.getProjectMd();
    if (!projectMd) return '';
    return `
## Project Instructions (PROJECT.md)
${projectMd}
`;
  }

  /** Build dynamic context (changes per turn — NOT cached) */
  buildDynamicContext() {
    const parts = [];
    parts.push(`Current date: ${new Date().toISOString().split('T')[0]}`);
    parts.push(`Working directory: ${this.config.noWorkspace ? 'No workspace selected' : this.config.workDir}`);
    if (this.config.noWorkspace) {
      parts.push('Workspace status: no project is open. Create one with VLGenerate or choose one with WorkspaceManager before making project-specific edits.');
    }
    if (this.projectContext.isVLProject()) {
      const s = this.projectContext.getSummary();
      parts.push(`VL Project: ${s?.projectName || 'detected'} (${s?.totalFiles || 0} files)`);
    }
    // Inject last compile errors if they exist (critical for AI debugging)
    const compileErrors = this._loadLastCompileErrors();
    if (compileErrors) {
      parts.push(compileErrors);
    }
    return parts.join('\n');
  }

  /** Load last compile errors from .vl-code/last-compile.json */
  _loadLastCompileErrors() {
    const compilePath = path.join(this.config.workDir, '.vl-code', 'last-compile.json');
    if (!fs.existsSync(compilePath)) return null;
    try {
      const data = JSON.parse(fs.readFileSync(compilePath, 'utf-8'));
      if (!data.errList?.length) return null;
      // Only inject if compile was recent (within last 30 minutes)
      const age = Date.now() - new Date(data.timestamp).getTime();
      if (age > 30 * 60 * 1000) return null;
      const errSummary = data.errList.slice(0, 30).map((e, i) => {
        if (typeof e === 'string') return `  ${i + 1}. ${e}`;
        if (typeof e === 'object') return `  ${i + 1}. [${e.file || e.type || '?'}] ${e.message || e.msg || JSON.stringify(e)}`;
        return `  ${i + 1}. ${JSON.stringify(e)}`;
      }).join('\n');
      return `\n⚠ LAST COMPILE ERRORS (${data.errCount} total, compiled ${data.timestamp}):\nThese errors were returned by the VL parsevl compiler. They indicate syntax or structural issues in the VL code that MUST be fixed.\n${errSummary}${data.errCount > 30 ? `\n  ... and ${data.errCount - 30} more (read .vl-code/last-compile.json for full list)` : ''}`;
    } catch { return null; }
  }

  /** Build system-reminder for first user message (project context injection) */
  buildInitialReminders() {
    const reminders = [];

    // VLCONFIG.md equivalent (project-level config)
    const vlConfig = this.projectContext.getVLConfig();
    if (vlConfig) {
      reminders.push(`<system-reminder>\n${vlConfig}\n</system-reminder>`);
    }

    // Project file summary
    if (this.projectContext.isVLProject()) {
      const summary = this.projectContext.getSummary();
      reminders.push(`<system-reminder>\nVL Project Context:\n${JSON.stringify(summary, null, 2)}\n</system-reminder>`);
    }

    // Inject .vl-code/rules/*.md content
    const rules = this.projectContext.getRules();
    if (rules.length > 0) {
      const rulesContent = rules.map(r => `### Rule: ${r.name}\n${r.content}`).join('\n\n');
      reminders.push(`<system-reminder>\nProject Rules:\n${rulesContent}\n</system-reminder>`);
    }

    // Auto-load MEMORY.md from project and global scopes
    const memoryContent = this._loadMemory();
    if (memoryContent) {
      reminders.push(`<system-reminder>\nPersistent Memory (from previous sessions):\n${memoryContent}\n</system-reminder>`);
    }

    // Inject active VL reference docs (cached from DocCenter)
    const vlDocsContent = this._loadVLReferenceDocs();
    if (vlDocsContent) {
      reminders.push(`<system-reminder>\nVL Reference Documentation (authoritative specs):\n${vlDocsContent}\n</system-reminder>`);
    }

    return reminders;
  }

  /** Load MEMORY.md from project scope and global scope */
  _loadMemory() {
    const parts = [];

    // Project-scope MEMORY.md
    const projectMemory = path.join(this.config.workDir, '.vl-code', 'memory', 'MEMORY.md');
    if (fs.existsSync(projectMemory)) {
      try {
        const content = fs.readFileSync(projectMemory, 'utf-8');
        // Limit to ~200 lines to avoid bloating context
        const trimmed = content.split('\n').slice(0, 200).join('\n');
        if (trimmed.trim()) parts.push(`## Project Memory\n${trimmed}`);
      } catch { /* non-critical */ }
    }

    // Global-scope MEMORY.md
    const globalMemory = path.join(os.homedir(), '.vl-code', 'memory', 'MEMORY.md');
    if (fs.existsSync(globalMemory)) {
      try {
        const content = fs.readFileSync(globalMemory, 'utf-8');
        const trimmed = content.split('\n').slice(0, 100).join('\n');
        if (trimmed.trim()) parts.push(`## Global Memory\n${trimmed}`);
      } catch { /* non-critical */ }
    }

    return parts.length > 0 ? parts.join('\n\n') : null;
  }

  /** Load active VL reference docs from global cache (~/.vl-code/docs/) */
  _loadVLReferenceDocs() {
    const docsDir = path.join(os.homedir(), '.vl-code', 'docs');
    const indexPath = path.join(docsDir, 'index.json');
    if (!fs.existsSync(indexPath)) return null;
    try {
      const index = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
      const activeDocs = index.filter(d => d.active);
      if (!activeDocs.length) return null;
      const parts = [];
      let totalLines = 0;
      const MAX_LINES = 3000; // Cap to prevent context overflow
      for (const doc of activeDocs) {
        const filePath = path.join(docsDir, doc.file);
        if (!fs.existsSync(filePath)) continue;
        const content = fs.readFileSync(filePath, 'utf-8');
        const lines = content.split('\n');
        const remaining = MAX_LINES - totalLines;
        if (remaining <= 0) break;
        const trimmed = lines.slice(0, remaining).join('\n');
        if (trimmed.trim()) {
          parts.push(`### ${doc.name}\n${trimmed}`);
          totalLines += Math.min(lines.length, remaining);
        }
      }
      return parts.length > 0 ? parts.join('\n\n---\n\n') : null;
    } catch { return null; }
  }

  /**
   * Build system-reminder for tool results (conditional injection)
   * Anti-forgetting mechanism: periodically reminds the LLM about key rules
   */
  buildToolReminder(toolName, context) {
    const reminders = [];

    // Todo reminder (if not used recently)
    if (context.turnsSinceTodo > 5 && context.hasPendingTasks) {
      reminders.push(`<system-reminder>
Consider using TodoWrite to track your progress on the current tasks.
</system-reminder>`);
    }

    // VL validation reminder after edits
    if (toolName === 'EditFile' && context.lastEditedFile?.match(/\.(vx|sc|cp|vs|vdb)$/)) {
      reminders.push(`<system-reminder>
You just edited a VL file. Consider running VLValidate to check syntax compliance.
</system-reminder>`);
    }

    // Blueprint consistency reminder (every 8 turns)
    if (context.turnCount > 0 && context.turnCount % 8 === 0 && context.hasBlueprints) {
      reminders.push(`<system-reminder>
Reminder: This project has Blueprint files (PRD/ServiceMap/UIMap). When adding new services or components, ensure they are consistent with the blueprints. Use VLImpact to check for inconsistencies.
</system-reminder>`);
    }

    // VL syntax rules reminder (every 12 turns for long conversations)
    if (context.turnCount > 10 && context.turnCount % 12 === 0) {
      reminders.push(`<system-reminder>
VL Critical Syntax Reminders (from VL Bible):
- Indentation uses dashes (-), NOT spaces or tabs
- Version header required: ${VL_VERSION_HEADER}
- No escape characters (\\n, \\t, \\\\) allowed anywhere
- Variables: $camelCase(TYPE) = value
- Components: <Type-Name "instanceId"> prop:value
- Methods: METHOD name(param(TYPE)); RETURN {field:TYPE}
- Events: EVENT @name(param(TYPE))
- FOR loops: FOR ($i(INT)=0; $i<$count; $i++) — ONLY place semicolons are used
- Section→Service calls: SERVICE DomainName.serviceName(params)
- Component instance-id MUST match between UIMap.json and actual .sc/.cp files
- Use VLSyntaxRef tool to look up widget APIs when generating UI code
- Always validate with VLValidate after edits
</system-reminder>`);
    }

    // Compile errors reminder — when there are unresolved compile errors
    if (context.hasCompileErrors && (toolName === 'WriteFile' || toolName === 'EditFile' || toolName === 'VLEditSection')) {
      reminders.push(`<system-reminder>
⚠ There are unresolved compile errors from the last VL compilation.
After making fixes, consider re-compiling to verify the errors are resolved.
Read .vl-code/last-compile.json for the full error list, or use VLSyntaxRef to check syntax rules.
</system-reminder>`);
    }

    // Prefer VLSymbols reminder when Grep is used on VL files
    if (toolName === 'Grep' && context.isVLProject) {
      reminders.push(`<system-reminder>
Tip: For finding VL symbol definitions and references, prefer VLSymbols over Grep — it's 900x faster and returns structured results.
</system-reminder>`);
    }

    return reminders.join('\n');
  }

  /** Build sub-agent system prompt (with VL Bible injection based on agent type) */
  buildSubAgentPrompt(agentType) {
    const vlBible = this._loadVLBible();
    const vlSection = vlBible ? this._getVLBibleForAgent(agentType, vlBible) : '';

    const base = `You are a sub-agent for VL-Code, specialized in VL language projects.
${vlSection}`;

    switch (agentType) {
      case 'explore':
        return `${base}\nYour job: search and analyze the VL codebase. Do NOT modify any files.\nReturn a concise summary of your findings.`;
      case 'plan':
        return `${base}\nYour job: design an implementation plan for VL code changes.\nIdentify key files, architectural trade-offs, and step-by-step approach.`;
      case 'generate':
        return `${base}\nYour job: generate VL code following strict VL ${VL_VERSION} syntax rules.\nAlways include ${VL_VERSION_HEADER} header. Follow naming conventions precisely.\nCRITICAL: All VL file names MUST be English-only PascalCase (e.g. ExpenseApp.vx, UserList.sc). NEVER use Chinese or non-ASCII characters in file names.\nWrite all files using WriteFile. Do NOT explain code — just generate and write.`;
      case 'general':
        return `${base}\nYou are a general-purpose agent with full read/write access.\nYou can read files, edit files, write files, run commands, and use all VL tools.\nExecute the assigned task autonomously and return a detailed summary of what you did.`;
      default:
        return `${base}\nComplete the assigned task and return a detailed summary.`;
    }
  }

  /**
   * Select appropriate VL Bible content for sub-agent type.
   * - generate/general: FULL Bible (they need complete widget catalog + syntax)
   * - explore/plan: condensed version (~30% of Bible — skip widget catalog)
   */
  _getVLBibleForAgent(agentType, fullBible) {
    if (agentType === 'generate' || agentType === 'general') {
      return `\n# VL Language Complete Reference\nVL is NOT in your training data — follow this reference precisely.\n\n${fullBible}\n`;
    }
    // For explore/plan: core syntax + hard rules, skip full widget catalog
    return `\n# VL Language Reference (condensed)\nVL is NOT in your training data — follow this reference precisely.\n\n${this._getCondensedBible(fullBible)}\n`;
  }

  /**
   * Condensed Bible: overview + syntax + naming + components + variables + logic + hard rules
   * Skips full widget catalog (~1200 lines) to save context for explore/plan agents.
   */
  _getCondensedBible(fullBible) {
    const lines = fullBible.split('\n');
    // Take everything up to and including the auth section (~line 2624)
    // Then append the hard rules section (line 2625 onwards, ~45 lines)
    // Skip the full widget catalog (lines 2670-3900)
    const core = lines.slice(0, 2670).join('\n');
    const hardRulesStart = lines.findIndex(l => /^##\s+VL Generation Hard Rules/i.test(l));
    if (hardRulesStart > 0) {
      // Hard rules section is ~45 lines
      const hardRules = lines.slice(hardRulesStart, hardRulesStart + 50).join('\n');
      return `${core}\n\n${hardRules}\n\n[Full widget catalog omitted — use VLSyntaxRef tool with action:"widget" to look up specific widget documentation]`;
    }
    return core;
  }

  /**
   * Load the VL Bible (complete syntax reference).
   * Search order: project-local > global (~/.vl-code/) > bundled (src/data/)
   * Cached in memory after first load for performance.
   */
  _loadVLBible() {
    if (this._vlBibleCache) return this._vlBibleCache;
    const candidates = [
      path.join(this.config.workDir, '.vl-code', 'vl-syntax.md'),
      path.join(os.homedir(), '.vl-code', 'vl-syntax.md'),
      path.join(__dirname, '..', 'data', 'vl-syntax.md'),
    ];
    for (const p of candidates) {
      if (fs.existsSync(p)) {
        try {
          this._vlBibleCache = fs.readFileSync(p, 'utf-8');
          // Strip version marker line if present
          if (this._vlBibleCache.startsWith('<!-- VL_SYNTAX_VERSION:')) {
            this._vlBibleCache = this._vlBibleCache.split('\n').slice(1).join('\n');
          }
          return this._vlBibleCache;
        } catch { /* continue to next candidate */ }
      }
    }
    return null;
  }
}
